From 063fed9ea329c7ad31deea9968e9969ce4809457 Mon Sep 17 00:00:00 2001 From: Patrick Wells Date: Mon, 27 Apr 2026 10:36:58 -0500 Subject: [PATCH 1/6] Add from_data to healsparse map --- healsparse/healSparseMap.py | 1153 ++++++++++++++++++++++++----------- 1 file changed, 801 insertions(+), 352 deletions(-) diff --git a/healsparse/healSparseMap.py b/healsparse/healSparseMap.py index 4f4f7e1..a43d302 100644 --- a/healsparse/healSparseMap.py +++ b/healsparse/healSparseMap.py @@ -1,16 +1,25 @@ -import numpy as np -import hpgeom as hpg import numbers +import warnings +import hpgeom as hpg +import numpy as np + +from .geom import GeomBase from .healSparseCoverage import HealSparseCoverage -from .utils import reduce_array, check_sentinel, _bitvals_to_packed_array -from .utils import WIDE_NBIT, WIDE_MASK, PIXEL_RANGE_THRESHOLD -from .utils import is_integer_value, _compute_bitshift -from .utils import has_duplicates, fast_unique from .io_map import _read_map, _write_map, _write_moc from .packedBoolArray import _PackedBoolArray -from .geom import GeomBase -import warnings +from .utils import ( + PIXEL_RANGE_THRESHOLD, + WIDE_MASK, + WIDE_NBIT, + _bitvals_to_packed_array, + _compute_bitshift, + check_sentinel, + fast_unique, + has_duplicates, + is_integer_value, + reduce_array, +) class HealSparseMap(object): @@ -18,9 +27,20 @@ class HealSparseMap(object): Class to define a HealSparseMap """ - def __init__(self, cov_map=None, cov_index_map=None, sparse_map=None, nside_sparse=None, - healpix_map=None, nside_coverage=None, primary=None, sentinel=None, - nest=True, metadata=None, _is_view=False): + def __init__( + self, + cov_map=None, + cov_index_map=None, + sparse_map=None, + nside_sparse=None, + healpix_map=None, + nside_coverage=None, + primary=None, + sentinel=None, + nest=True, + metadata=None, + _is_view=False, + ): """ Instantiate a HealSparseMap. @@ -60,7 +80,7 @@ def __init__(self, cov_map=None, cov_index_map=None, sparse_map=None, nside_spar healSparseMap : `HealSparseMap` """ if cov_index_map is not None and cov_map is not None: - raise RuntimeError('Cannot specify both cov_index_map and cov_map') + raise RuntimeError("Cannot specify both cov_index_map and cov_map") if cov_index_map is not None: warnings.warn("cov_index_map deprecated", DeprecationWarning, stacklevel=2) cov_map = HealSparseCoverage(cov_index_map, nside_sparse) @@ -74,17 +94,22 @@ def __init__(self, cov_map=None, cov_index_map=None, sparse_map=None, nside_spar if sentinel is None: sentinel = hpg.UNSEEN if is_integer_value(healpix_map[0]) and not is_integer_value(sentinel): - raise ValueError("The sentinel must be set to an integer value with an integer healpix_map") + raise ValueError( + "The sentinel must be set to an integer value with an integer healpix_map" + ) elif not is_integer_value(healpix_map[0]) and is_integer_value(sentinel): - raise ValueError("The sentinel must be set to an float value with an float healpix_map") + raise ValueError( + "The sentinel must be set to an float value with an float healpix_map" + ) - self._cov_map, self._sparse_map = self.convert_healpix_map(healpix_map, - nside_coverage=nside_coverage, - nest=nest, - sentinel=sentinel) + self._cov_map, self._sparse_map = self.convert_healpix_map( + healpix_map, nside_coverage=nside_coverage, nest=nest, sentinel=sentinel + ) nside_sparse = hpg.npixel_to_nside(healpix_map.size) else: - raise RuntimeError("Must specify either cov_map/sparse_map or healpix_map/nside_coverage") + raise RuntimeError( + "Must specify either cov_map/sparse_map or healpix_map/nside_coverage" + ) self._nside_sparse = nside_sparse @@ -99,28 +124,47 @@ def __init__(self, cov_map=None, cov_index_map=None, sparse_map=None, nside_spar if self._sparse_map.dtype.fields is not None: self._is_rec_array = True if self._primary is None: - raise RuntimeError("Must specify `primary` field when using a recarray for the sparse_map.") + raise RuntimeError( + "Must specify `primary` field when using a recarray for the sparse_map." + ) - self._sentinel = check_sentinel(self._sparse_map[self._primary].dtype.type, sentinel) + self._sentinel = check_sentinel( + self._sparse_map[self._primary].dtype.type, sentinel + ) else: - if ((self._sparse_map.dtype.type == WIDE_MASK) and len(self._sparse_map.shape) == 2): + if (self._sparse_map.dtype.type == WIDE_MASK) and len( + self._sparse_map.shape + ) == 2: self._is_wide_mask = True self._wide_mask_width = self._sparse_map.shape[1] self._wide_mask_maxbits = WIDE_NBIT * self._wide_mask_width elif isinstance(self._sparse_map, _PackedBoolArray): self._is_bit_packed = True if sentinel is not False: - raise NotImplementedError("Can only use False sentinel for bit_packed maps.") + raise NotImplementedError( + "Can only use False sentinel for bit_packed maps." + ) if (self._cov_map.nfine_per_cov % 8) != 0: - raise ValueError("Can only create a bit_packed map at least two " - "healpix levels between coverage and mask.") + raise ValueError( + "Can only create a bit_packed map at least two " + "healpix levels between coverage and mask." + ) self._sentinel = check_sentinel(self._sparse_map.dtype.type, sentinel) @classmethod - def read(cls, filename, nside_coverage=None, pixels=None, header=False, - degrade_nside=None, weightfile=None, reduction='mean', - use_threads=False, hdf5_group='map'): + def read( + cls, + filename, + nside_coverage=None, + pixels=None, + header=False, + degrade_nside=None, + weightfile=None, + reduction="mean", + use_threads=False, + hdf5_group="map", + ): """ Read in a HealSparseMap. @@ -160,14 +204,32 @@ def read(cls, filename, nside_coverage=None, pixels=None, header=False, header : `fitsio.FITSHDR` or `astropy.io.fits` (if header=True) Fits header for the map file. """ - return _read_map(cls, filename, nside_coverage=nside_coverage, pixels=pixels, - header=header, degrade_nside=degrade_nside, - weightfile=weightfile, reduction=reduction, use_threads=use_threads, - hdf5_group=hdf5_group) + return _read_map( + cls, + filename, + nside_coverage=nside_coverage, + pixels=pixels, + header=header, + degrade_nside=degrade_nside, + weightfile=weightfile, + reduction=reduction, + use_threads=use_threads, + hdf5_group=hdf5_group, + ) @classmethod - def make_empty(cls, nside_coverage, nside_sparse, dtype, primary=None, sentinel=None, - wide_mask_maxbits=None, metadata=None, cov_pixels=None, bit_packed=False): + def make_empty( + cls, + nside_coverage, + nside_sparse, + dtype, + primary=None, + sentinel=None, + wide_mask_maxbits=None, + metadata=None, + cov_pixels=None, + bit_packed=False, + ): """ Make an empty map with nothing in it. @@ -202,7 +264,9 @@ def make_empty(cls, nside_coverage, nside_sparse, dtype, primary=None, sentinel= if wide_mask_maxbits is not None: if test_arr.dtype != WIDE_MASK: - raise ValueError("Must use dtype=healsparse.WIDE_MASK to use a wide_mask") + raise ValueError( + "Must use dtype=healsparse.WIDE_MASK to use a wide_mask" + ) if sentinel is not None: if sentinel != 0: raise ValueError("Sentinel must be 0 for wide_mask") @@ -217,31 +281,40 @@ def make_empty(cls, nside_coverage, nside_sparse, dtype, primary=None, sentinel= npix = 1 else: cov_pixels = np.atleast_1d(cov_pixels) - cov_map = HealSparseCoverage.make_from_pixels(nside_coverage, nside_sparse, - cov_pixels) + cov_map = HealSparseCoverage.make_from_pixels( + nside_coverage, nside_sparse, cov_pixels + ) # We need to allocate the overflow pixel npix = cov_pixels.size + 1 if wide_mask_maxbits is not None: # The sentinel is always zero _sentinel = 0 - sparse_map = np.zeros((cov_map.nfine_per_cov*npix, nbitfields), dtype=dtype) + sparse_map = np.zeros( + (cov_map.nfine_per_cov * npix, nbitfields), dtype=dtype + ) elif bit_packed: _sentinel = check_sentinel(test_arr.dtype.type, sentinel) if (cov_map.nfine_per_cov % 8) != 0: - raise ValueError("Can only create a bit_packed mask at least two " - "healpix levels between coverage and mask.") + raise ValueError( + "Can only create a bit_packed mask at least two " + "healpix levels between coverage and mask." + ) if _sentinel: - raise NotImplementedError("Can only create a bit_packed map with False sentinel value.") - sparse_map = _PackedBoolArray(size=cov_map.nfine_per_cov*npix) + raise NotImplementedError( + "Can only create a bit_packed map with False sentinel value." + ) + sparse_map = _PackedBoolArray(size=cov_map.nfine_per_cov * npix) elif test_arr.dtype.fields is None: # Non-recarray _sentinel = check_sentinel(test_arr.dtype.type, sentinel) - sparse_map = np.full(cov_map.nfine_per_cov*npix, _sentinel, dtype=dtype) + sparse_map = np.full(cov_map.nfine_per_cov * npix, _sentinel, dtype=dtype) else: # Recarray type if primary is None: - raise RuntimeError("Must specify 'primary' field when using a recarray for the sparse_map.") + raise RuntimeError( + "Must specify 'primary' field when using a recarray for the sparse_map." + ) primary_found = False for name in test_arr.dtype.names: @@ -253,18 +326,35 @@ def make_empty(cls, nside_coverage, nside_sparse, dtype, primary=None, sentinel= test_arr[name] = check_sentinel(test_arr[name].dtype.type, None) if not primary_found: - raise RuntimeError("Primary field not found in input dtype of recarray.") + raise RuntimeError( + "Primary field not found in input dtype of recarray." + ) - sparse_map = np.full(cov_map.nfine_per_cov*npix, test_arr, dtype=dtype) + sparse_map = np.full(cov_map.nfine_per_cov * npix, test_arr, dtype=dtype) - return cls(cov_map=cov_map, sparse_map=sparse_map, - nside_sparse=nside_sparse, primary=primary, sentinel=_sentinel, - metadata=metadata) + return cls( + cov_map=cov_map, + sparse_map=sparse_map, + nside_sparse=nside_sparse, + primary=primary, + sentinel=_sentinel, + metadata=metadata, + ) @classmethod - def make_empty_like(cls, sparsemap, nside_coverage=None, nside_sparse=None, dtype=None, - primary=None, sentinel=None, wide_mask_maxbits=None, metadata=None, - cov_pixels=None, bit_packed=False): + def make_empty_like( + cls, + sparsemap, + nside_coverage=None, + nside_sparse=None, + dtype=None, + primary=None, + sentinel=None, + wide_mask_maxbits=None, + metadata=None, + cov_pixels=None, + bit_packed=False, + ): """ Make an empty map with the same parameters as an existing map. @@ -314,12 +404,68 @@ def make_empty_like(cls, sparsemap, nside_coverage=None, nside_sparse=None, dtyp if metadata is None: metadata = sparsemap._metadata - return cls.make_empty(nside_coverage, nside_sparse, dtype, primary=primary, - sentinel=sentinel, wide_mask_maxbits=wide_mask_maxbits, - metadata=metadata, cov_pixels=cov_pixels, bit_packed=bit_packed) + return cls.make_empty( + nside_coverage, + nside_sparse, + dtype, + primary=primary, + sentinel=sentinel, + wide_mask_maxbits=wide_mask_maxbits, + metadata=metadata, + cov_pixels=cov_pixels, + bit_packed=bit_packed, + ) + + @staticmethod + def from_data( + pixels, data, nside_coverage, nside_sparse, nest=True, sentinel=hpg.UNSEEN + ): + """ + Create a healsparse map from a partial healpix map. + + Parameters: + ----------- + pixels : `np.ndarray` or `list` + The pixels covered by the data. Must be the same length as `data`. + data : `np.ndarray` or `list` + The data for the given pixels. Must be the same length as `pixels`. + nside_coverage : `int` + Coverage nside + nside_sparse : `int`, optional + Sparse map nside + nest : `bool`, optional + Is the input map in nest format? Default is True. If False, data will + be converted to nested format. + sentinel : `float`, optional + Sentinel value for null values in the sparse_map. + """ + if len(pixels) != len(data): + raise ValueError("pixel array and data array must be the same length!") + + if not nest: + full_map = np.full(hpg.nside2npix(nside_sparse), sentinel, dtype=data.dtype) + full_map[pixels] = data + full_map = hpg.reorder(full_map, ring_to_nest=True) + pixels = np.where(full_map != sentinel)[0] + data = full_map[pixels] + + cov_map = HealSparseCoverage.make_empty(nside_coverage, nside_sparse) + cov_pix = cov_map.cov_pixels(pixels) + unique_cov_pix = np.unique(cov_pix) + + cov_map.initialize_pixels(unique_cov_pix) + sparse_indices = pixels + cov_map[cov_pix] + sparse_map_size = (len(unique_cov_pix) + 1) * cov_map.nfine_per_cov + + sparse_map = np.full(sparse_map_size, sentinel, dtype=data.dtype) + sparse_map[sparse_indices] = data + + return sparse_map @staticmethod - def convert_healpix_map(healpix_map, nside_coverage, nest=True, sentinel=hpg.UNSEEN): + def convert_healpix_map( + healpix_map, nside_coverage, nest=True, sentinel=hpg.UNSEEN + ): """ Convert a healpix map to a healsparsemap. @@ -347,7 +493,7 @@ def convert_healpix_map(healpix_map, nside_coverage, nest=True, sentinel=hpg.UNS # Compute the coverage map... # Note that this is coming from a standard healpix map so the sentinel # is always hpg.UNSEEN - ipnest, = np.where(healpix_map > hpg.UNSEEN) + (ipnest,) = np.where(healpix_map > hpg.UNSEEN) nside_sparse = hpg.npixel_to_nside(healpix_map.size) cov_map = HealSparseCoverage.make_empty(nside_coverage, nside_sparse) @@ -357,13 +503,24 @@ def convert_healpix_map(healpix_map, nside_coverage, nest=True, sentinel=hpg.UNS cov_map.initialize_pixels(cov_pix) - sparse_map = np.full((cov_pix.size + 1)*cov_map.nfine_per_cov, - sentinel, dtype=healpix_map.dtype) + sparse_map = np.full( + (cov_pix.size + 1) * cov_map.nfine_per_cov, + sentinel, + dtype=healpix_map.dtype, + ) sparse_map[ipnest + cov_map[ipnest_cov]] = healpix_map[ipnest] return cov_map, sparse_map - def write(self, filename, clobber=False, nocompress=False, format='fits', nside_io=4, hdf5_group='map'): + def write( + self, + filename, + clobber=False, + nocompress=False, + format="fits", + nside_io=4, + hdf5_group="map", + ): """ Write a HealSparseMap to a file. Use the `metadata` property from the map to persist additional information in the fits header. @@ -394,8 +551,15 @@ def write(self, filename, clobber=False, nocompress=False, format='fits', nside_ NotImplementedError if file format is not supported. ValueError if nside_io is out of range. """ - _write_map(self, filename, clobber=clobber, nocompress=nocompress, format=format, - nside_io=nside_io, hdf5_group=hdf5_group) + _write_map( + self, + filename, + clobber=clobber, + nocompress=nocompress, + format=format, + nside_io=nside_io, + hdf5_group=hdf5_group, + ) def write_moc(self, filename, clobber=False): """ @@ -422,12 +586,14 @@ def _reserve_cov_pix(self, new_cov_pix): Integer array of new coverage pixels """ - new_cov_map = self._cov_map.append_pixels(len(self._sparse_map), new_cov_pix, check=False) + new_cov_map = self._cov_map.append_pixels( + len(self._sparse_map), new_cov_pix, check=False + ) self._cov_map = new_cov_map # Use resizing oldsize = len(self._sparse_map) - newsize = oldsize + new_cov_pix.size*self._cov_map.nfine_per_cov + newsize = oldsize + new_cov_pix.size * self._cov_map.nfine_per_cov if self._is_wide_mask: self._sparse_map.resize((newsize, self._wide_mask_width), refcheck=False) @@ -437,8 +603,15 @@ def _reserve_cov_pix(self, new_cov_pix): # Fill with blank values self._sparse_map[oldsize:] = self._sparse_map[0] - def update_values_pos(self, ra_or_theta, dec_or_phi, values, - lonlat=True, operation='replace', check_unique=True): + def update_values_pos( + self, + ra_or_theta, + dec_or_phi, + values, + lonlat=True, + operation="replace", + check_unique=True, + ): """ Update the values in the sparsemap for a list of positions. @@ -485,7 +658,9 @@ def update_values_pos(self, ra_or_theta, dec_or_phi, values, check_unique=check_unique, ) - def update_values_pix(self, pixels, values, nest=True, operation='replace', check_unique=True): + def update_values_pix( + self, pixels, values, nest=True, operation="replace", check_unique=True + ): """ Update the values in the sparsemap for a list of pixels. The list of pixels must be unique if the operation is 'replace'. @@ -525,7 +700,7 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec # When None is specified, we use the sentinel value. no_append = False if values is None: - if operation != 'replace': + if operation != "replace": raise ValueError("Can only use 'None' with 'replace' operation.") if self._is_wide_mask: @@ -537,18 +712,24 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec values = self._sentinel no_append = True - if operation != 'replace': + if operation != "replace": if self.dtype == np.bool_: - if operation not in ['or', 'and']: - raise NotImplementedError("Booleam maps Can only use replace/and/or operations.") - elif operation in ['or', 'and']: + if operation not in ["or", "and"]: + raise NotImplementedError( + "Booleam maps Can only use replace/and/or operations." + ) + elif operation in ["or", "and"]: if not self.is_integer_map or self._sentinel != 0: - raise ValueError("Can only use and/or with integer map with 0 sentinel") - elif operation == 'add': + raise ValueError( + "Can only use and/or with integer map with 0 sentinel" + ) + elif operation == "add": if self._is_rec_array: raise ValueError("Cannot use 'add' operation with a recarray map.") else: - raise ValueError("Only 'replace', 'add', 'or', and 'and' are supported operations") + raise ValueError( + "Only 'replace', 'add', 'or', and 'and' are supported operations" + ) # If _not_ recarray, we can use a single int/float is_single_value = False @@ -571,7 +752,9 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec _values = np.asarray([values], dtype=self.dtype) elif isinstance(values, numbers.Real): if self.is_integer_map: - raise ValueError("Cannot set non-floating point map with a floating point.") + raise ValueError( + "Cannot set non-floating point map with a floating point." + ) is_single_value = True _values = np.asarray([values], dtype=self.dtype) elif isinstance(values, (bool, np.bool_)): @@ -587,9 +770,11 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec if hasattr(pixels, "__len__") and len(pixels) == 0: if len(_values) > 1: - warnings.warn("Shape mismatch: using a non-zero-length array of values " - "to set a zero-length list of pixels.", - UserWarning) + warnings.warn( + "Shape mismatch: using a non-zero-length array of values " + "to set a zero-length list of pixels.", + UserWarning, + ) # Nothing to do return @@ -601,15 +786,19 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec elif self._sparse_map.dtype.type != _values.dtype.type: raise ValueError("Data-type mismatch between sparse_map and values") - if operation == 'replace' and check_unique: + if operation == "replace" and check_unique: if hasattr(pixels, "__len__"): if has_duplicates(pixels): - raise ValueError("List of pixels must be unique if operation='replace'") + raise ValueError( + "List of pixels must be unique if operation='replace'" + ) if pixels.ndim == 2 and pixels.shape[1] == 2: # These are pixel ranges. if not is_single_value: - raise ValueError("Can only use a single value with pixel ranges (N, 2) input.") + raise ValueError( + "Can only use a single value with pixel ranges (N, 2) input." + ) if not nest: raise ValueError("Can only use pixel ranges with nest ordering.") @@ -617,7 +806,9 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec # if the number of pixels is above some threshold. pixels_to_set = np.sum(pixels[:, 1] - pixels[:, 0]) if pixels_to_set > PIXEL_RANGE_THRESHOLD: - return self._update_values_pixel_ranges(pixels, _values[0], operation, no_append) + return self._update_values_pixel_ranges( + pixels, _values[0], operation, no_append + ) else: _pix = hpg.pixel_ranges_to_pixels(pixels) elif not nest: @@ -627,12 +818,16 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace', chec # Check array lengths if not is_single_value and len(_values) != pixels.size: - raise ValueError("Length of values must be same length as pixels (or length 1)") + raise ValueError( + "Length of values must be same length as pixels (or length 1)" + ) if self._is_view: # Check that we are not setting new pixels if np.any(self.get_values_pix(_pix) == self._sentinel): - raise RuntimeError("This API cannot be used to set new pixels in the map.") + raise RuntimeError( + "This API cannot be used to set new pixels in the map." + ) # Compute the coverage pixels ipnest_cov = self._cov_map.cov_pixels(_pix) @@ -666,9 +861,13 @@ def _do_operation_on_sparse_map(operation, sparse_map, indices, values): # Replace values for those pixels in the coverage map _indices = _pix[in_cov] + self._cov_map[ipnest_cov[in_cov]] if is_single_value: - _do_operation_on_sparse_map(operation, self._sparse_map, _indices, _values[0]) + _do_operation_on_sparse_map( + operation, self._sparse_map, _indices, _values[0] + ) else: - _do_operation_on_sparse_map(operation, self._sparse_map, _indices, _values[in_cov]) + _do_operation_on_sparse_map( + operation, self._sparse_map, _indices, _values[in_cov] + ) # Update the coverage map for the rest of the pixels (if necessary) if out_cov.sum() > 0 and not no_append: @@ -677,7 +876,7 @@ def _do_operation_on_sparse_map(operation, sparse_map, indices, values): # Faster trick for getting unique values new_cov_temp = np.zeros(cov_mask.size, dtype=np.int8) new_cov_temp[ipnest_cov[out_cov]] = 1 - new_cov_pix, = np.where(new_cov_temp > 0) + (new_cov_pix,) = np.where(new_cov_temp > 0) # Reserve the memory here oldsize = len(self._sparse_map) @@ -685,9 +884,13 @@ def _do_operation_on_sparse_map(operation, sparse_map, indices, values): _indices = _pix[out_cov] + self._cov_map[ipnest_cov[out_cov]] - oldsize if is_single_value: - _do_operation_on_sparse_map(operation, self._sparse_map[oldsize:], _indices, _values[0]) + _do_operation_on_sparse_map( + operation, self._sparse_map[oldsize:], _indices, _values[0] + ) else: - _do_operation_on_sparse_map(operation, self._sparse_map[oldsize:], _indices, _values[out_cov]) + _do_operation_on_sparse_map( + operation, self._sparse_map[oldsize:], _indices, _values[out_cov] + ) def _update_values_pixel_ranges(self, pixel_ranges, value, operation, no_append): """ @@ -728,28 +931,31 @@ def _update_values_pixel_ranges(self, pixel_ranges, value, operation, no_append) delta_pix = pixel_ranges[:, 1] - pixel_ranges[:, 0] delta_covpix = cov_pix_ranges[:, 1] - cov_pix_ranges[:, 0] - covpix_start_values = (self._cov_map[cov_pix_ranges.ravel()] + - self._cov_map.nfine_per_cov*cov_pix_ranges.ravel() - ).reshape(cov_pix_ranges.shape) + covpix_start_values = ( + self._cov_map[cov_pix_ranges.ravel()] + + self._cov_map.nfine_per_cov * cov_pix_ranges.ravel() + ).reshape(cov_pix_ranges.shape) - covpix_offset_values = self._cov_map[self._cov_map.cov_pixels_from_index( - covpix_start_values.ravel() - )].reshape(cov_pix_ranges.shape) + covpix_offset_values = self._cov_map[ + self._cov_map.cov_pixels_from_index(covpix_start_values.ravel()) + ].reshape(cov_pix_ranges.shape) - def _do_operation_on_sparse_map_range(operation, sparse_map, start, stop, value): + def _do_operation_on_sparse_map_range( + operation, sparse_map, start, stop, value + ): # Note that start: stop will not have overlapping pixels, so we do # not need to use ufunc.at() to perform operations. if operation == "replace": - sparse_map[start: stop] = value + sparse_map[start:stop] = value elif operation == "add": # Put in a check to reset uncovered pixels to 0 if self._sentinel != 0: - sparse_map[start: stop][sparse_map[start: stop] == self._sentinel] = 0 - sparse_map[start: stop] += value + sparse_map[start:stop][sparse_map[start:stop] == self._sentinel] = 0 + sparse_map[start:stop] += value elif operation == "or": - sparse_map[start: stop] |= value + sparse_map[start:stop] |= value elif operation == "and": - sparse_map[start: stop] &= value + sparse_map[start:stop] &= value # Loop over ranges. for i in range(pixel_ranges.shape[0]): @@ -761,31 +967,42 @@ def _do_operation_on_sparse_map_range(operation, sparse_map, start, stop, value) else: # The first coverage pixel will be partly covered. start = pixel_ranges[i, 0] + covpix_offset_values[i, 0] - stop = ( - self._cov_map[cov_pix_ranges[i, 0]] + - self._cov_map.nfine_per_cov*(cov_pix_ranges[i, 0] + 1) + stop = self._cov_map[ + cov_pix_ranges[i, 0] + ] + self._cov_map.nfine_per_cov * (cov_pix_ranges[i, 0] + 1) + _do_operation_on_sparse_map_range( + operation, self._sparse_map, start, stop, value ) - _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) # The middle coverage pixels will be fully covered. - for cov_pix_full in range(cov_pix_ranges[i, 0] + 1, cov_pix_ranges[i, 1]): + for cov_pix_full in range( + cov_pix_ranges[i, 0] + 1, cov_pix_ranges[i, 1] + ): if no_append and not cov_mask[cov_pix_full]: # Nothing to set here. continue - start = (self._cov_map[cov_pix_full] + self._cov_map.nfine_per_cov*cov_pix_full) + start = ( + self._cov_map[cov_pix_full] + + self._cov_map.nfine_per_cov * cov_pix_full + ) stop = start + self._cov_map.nfine_per_cov - _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) + _do_operation_on_sparse_map_range( + operation, self._sparse_map, start, stop, value + ) if no_append and not cov_mask[cov_pix_ranges[i, 1]]: # Nothing to set here. pass else: # The final coverage pixel will be partly covered. - start = (self._cov_map[cov_pix_ranges[i, 1]] + - self._cov_map.nfine_per_cov*(cov_pix_ranges[i, 1]) - ) + start = ( + self._cov_map[cov_pix_ranges[i, 1]] + + self._cov_map.nfine_per_cov * (cov_pix_ranges[i, 1]) + ) stop = pixel_ranges[i, 1] + covpix_offset_values[i, 1] - _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) + _do_operation_on_sparse_map_range( + operation, self._sparse_map, start, stop, value + ) else: if no_append and not cov_mask[cov_pix_ranges[i, 0]]: # Nothing to set here. @@ -795,7 +1012,9 @@ def _do_operation_on_sparse_map_range(operation, sparse_map, start, stop, value) start = pixel_ranges[i, 0] + covpix_offset_values[i, 0] stop = start + delta_pix[i] - _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) + _do_operation_on_sparse_map_range( + operation, self._sparse_map, start, stop, value + ) def set_bits_pix(self, pixels, bits, nest=True): """ @@ -812,12 +1031,14 @@ def set_bits_pix(self, pixels, bits, nest=True): raise NotImplementedError("Can only use set_bits_pix on wide_mask map") if np.max(bits) >= self._wide_mask_maxbits: - raise ValueError("Bit position %d too large (>= %d)" % (np.max(bits), - self._wide_mask_maxbits)) + raise ValueError( + "Bit position %d too large (>= %d)" + % (np.max(bits), self._wide_mask_maxbits) + ) value = _bitvals_to_packed_array(bits, self._wide_mask_maxbits) - self.update_values_pix(pixels, value, nest=nest, operation='or') + self.update_values_pix(pixels, value, nest=nest, operation="or") def clear_bits_pix(self, pixels, bits, nest=True): """ @@ -834,13 +1055,15 @@ def clear_bits_pix(self, pixels, bits, nest=True): raise NotImplementedError("Can only use set_bits_pix on wide_mask map") if np.max(bits) >= self._wide_mask_maxbits: - raise ValueError("Bit position %d too large (>= %d)" % (np.max(bits), - self._wide_mask_maxbits)) + raise ValueError( + "Bit position %d too large (>= %d)" + % (np.max(bits), self._wide_mask_maxbits) + ) value = _bitvals_to_packed_array(bits, self._wide_mask_maxbits) # A bit reset is performed with &= ~(bit1 | bit2) - self.update_values_pix(pixels, ~value, nest=nest, operation='and') + self.update_values_pix(pixels, ~value, nest=nest, operation="and") def get_values_pos(self, ra_or_theta, dec_or_phi, lonlat=True, valid_mask=False): """ @@ -865,11 +1088,12 @@ def get_values_pos(self, ra_or_theta, dec_or_phi, lonlat=True, valid_mask=False) values : `np.ndarray` Array of values/validity from the map. """ - return self.get_values_pix(hpg.angle_to_pixel(self._nside_sparse, - ra_or_theta, - dec_or_phi, - lonlat=lonlat), - valid_mask=valid_mask) + return self.get_values_pix( + hpg.angle_to_pixel( + self._nside_sparse, ra_or_theta, dec_or_phi, lonlat=lonlat + ), + valid_mask=valid_mask, + ) def get_values_pix(self, pixels, nest=True, valid_mask=False, nside=None): """ @@ -922,11 +1146,11 @@ def get_values_pix(self, pixels, nest=True, valid_mask=False, nside=None): if valid_mask: if self._is_rec_array: - return (values[self._primary] != self._sentinel) + return values[self._primary] != self._sentinel elif self._is_wide_mask: return (values > 0).sum(axis=1, dtype=np.bool_) else: - return (values != self._sentinel) + return values != self._sentinel else: # Just return the values return values @@ -955,11 +1179,12 @@ def check_bits_pos(self, ra_or_theta, dec_or_phi, bits, lonlat=True): Array of `np.bool_` flags on whether any of the input bits were set """ - return self.check_bits_pix(hpg.angle_to_pixel(self._nside_sparse, - ra_or_theta, - dec_or_phi, - lonlat=lonlat), - bits) + return self.check_bits_pix( + hpg.angle_to_pixel( + self._nside_sparse, ra_or_theta, dec_or_phi, lonlat=lonlat + ), + bits, + ) def check_bits_pix(self, pixels, bits, nest=True): """ @@ -1015,27 +1240,27 @@ def coverage_map(self): cov_mask = self.coverage_mask npop_pix = np.count_nonzero(cov_mask) if self._is_wide_mask: - shape_new = (npop_pix + 1, - self._cov_map.nfine_per_cov, - self._wide_mask_width) + shape_new = ( + npop_pix + 1, + self._cov_map.nfine_per_cov, + self._wide_mask_width, + ) sp_map_t = self._sparse_map.reshape(shape_new) # This trickery first checks all the bits, and then sums into the # coverage pixel counts = np.sum(np.any(sp_map_t != self._sentinel, axis=2), axis=1) elif self._is_bit_packed: - shape_new = (npop_pix + 1, - self._cov_map.nfine_per_cov) + shape_new = (npop_pix + 1, self._cov_map.nfine_per_cov) counts = self._sparse_map.sum(shape=shape_new, axis=1).astype(np.float64) else: - shape_new = (npop_pix + 1, - self._cov_map.nfine_per_cov) + shape_new = (npop_pix + 1, self._cov_map.nfine_per_cov) if self._is_rec_array: sp_map_t = self._sparse_map[self._primary].reshape(shape_new) else: sp_map_t = self._sparse_map.reshape(shape_new) counts = np.sum((sp_map_t != self._sentinel), axis=1).astype(np.float64) - cov_map[cov_mask] = counts[1:]/self._cov_map.nfine_per_cov + cov_map[cov_mask] = counts[1:] / self._cov_map.nfine_per_cov return cov_map @property @@ -1072,11 +1297,15 @@ def fracdet_map(self, nside): Fractional coverage map. """ if nside > self.nside_sparse: - raise ValueError("Cannot return fracdet_map at higher resolution than " - "the sparse map (nside=%d)." % (self.nside_sparse)) + raise ValueError( + "Cannot return fracdet_map at higher resolution than " + "the sparse map (nside=%d)." % (self.nside_sparse) + ) if nside < self.nside_coverage: - raise ValueError("Cannot return fractdet_map at lower resolution than " - "the coverage map (nside=%d)." % (self.nside_coverage)) + raise ValueError( + "Cannot return fractdet_map at lower resolution than " + "the coverage map (nside=%d)." % (self.nside_coverage) + ) # This code is essentially a unification of coverage_map() and degrade() # to get the fracdet_coverage in a single step @@ -1085,20 +1314,23 @@ def fracdet_map(self, nside): bit_shift = _compute_bitshift(nside, self.nside_sparse) nfine_per_frac = 2**bit_shift - nfrac_per_cov = self._cov_map.nfine_per_cov//nfine_per_frac + nfrac_per_cov = self._cov_map.nfine_per_cov // nfine_per_frac if self._is_wide_mask: - shape_new = ((npop_pix + 1)*nfrac_per_cov, - nfine_per_frac, - self._wide_mask_width) + shape_new = ( + (npop_pix + 1) * nfrac_per_cov, + nfine_per_frac, + self._wide_mask_width, + ) sp_map_t = self._sparse_map.reshape(shape_new) - fracdet = np.sum(np.any(sp_map_t != self._sentinel, axis=2), axis=1).astype(np.float64) + fracdet = np.sum(np.any(sp_map_t != self._sentinel, axis=2), axis=1).astype( + np.float64 + ) elif self._is_bit_packed: - shape_new = ((npop_pix + 1)*nfrac_per_cov, nfine_per_frac) + shape_new = ((npop_pix + 1) * nfrac_per_cov, nfine_per_frac) fracdet = self._sparse_map.sum(shape=shape_new, axis=1).astype(np.float64) else: - shape_new = ((npop_pix + 1)*nfrac_per_cov, - nfine_per_frac) + shape_new = ((npop_pix + 1) * nfrac_per_cov, nfine_per_frac) if self._is_rec_array: sp_map_t = self._sparse_map[self._primary].reshape(shape_new) else: @@ -1107,14 +1339,18 @@ def fracdet_map(self, nside): fracdet /= nfine_per_frac - fracdet_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, - nside, - np.where(cov_mask)[0]) + fracdet_cov_map = HealSparseCoverage.make_from_pixels( + self.nside_coverage, nside, np.where(cov_mask)[0] + ) # The sentinel for a fracdet_map is 0.0, no coverage. - return HealSparseMap(cov_map=fracdet_cov_map, sparse_map=fracdet, - nside_sparse=nside, primary=self._primary, - sentinel=0.0) + return HealSparseMap( + cov_map=fracdet_cov_map, + sparse_map=fracdet, + nside_sparse=nside, + primary=self._primary, + sentinel=0.0, + ) @property def nside_coverage(self): @@ -1281,7 +1517,7 @@ def metadata(self, metadata): self._metadata = metadata - def generate_healpix_map(self, nside=None, reduction='mean', key=None, nest=True): + def generate_healpix_map(self, nside=None, reduction="mean", key=None, nest=True): """ Generate the associated healpix map @@ -1315,7 +1551,9 @@ def generate_healpix_map(self, nside=None, reduction='mean', key=None, nest=True if self._is_rec_array: if key is None: - raise ValueError('key should be specified for HealSparseMaps including `recarray`') + raise ValueError( + "key should be specified for HealSparseMaps including `recarray`" + ) else: # This is memory inefficient in that we are copying the memory # to ensure that we get a unique healpix map. To not get a copy, @@ -1331,7 +1569,9 @@ def generate_healpix_map(self, nside=None, reduction='mean', key=None, nest=True # degrade to new resolution single_map = single_map.degrade(nside, reduction=reduction) elif nside > self._nside_sparse: - raise ValueError("Cannot generate HEALPix map with higher resolution than the original.") + raise ValueError( + "Cannot generate HEALPix map with higher resolution than the original." + ) # Check to see if we have an integer or boolean map. if issubclass(single_map._sparse_map.dtype.type, (np.integer, np.bool_, bool)): @@ -1359,17 +1599,26 @@ def valid_pixels(self): valid_pixels : `np.ndarray` """ if self._is_rec_array: - valid_pixel_inds, = np.where(self._sparse_map[self._primary] != self._sentinel) + (valid_pixel_inds,) = np.where( + self._sparse_map[self._primary] != self._sentinel + ) elif self._is_wide_mask: - valid_pixel_inds, = np.where(np.any(self._sparse_map != self._sentinel, axis=1)) + (valid_pixel_inds,) = np.where( + np.any(self._sparse_map != self._sentinel, axis=1) + ) elif self._is_bit_packed: # This is dangerous because it expands into a full array first; this # can blow up memory. - valid_pixel_inds, = np.where(np.asarray(self._sparse_map) != self._sentinel) + (valid_pixel_inds,) = np.where( + np.asarray(self._sparse_map) != self._sentinel + ) else: - valid_pixel_inds, = np.where(self._sparse_map != self._sentinel) + (valid_pixel_inds,) = np.where(self._sparse_map != self._sentinel) - return valid_pixel_inds - self._cov_map[self._cov_map.cov_pixels_from_index(valid_pixel_inds)] + return ( + valid_pixel_inds + - self._cov_map[self._cov_map.cov_pixels_from_index(valid_pixel_inds)] + ) def valid_pixels_pos(self, lonlat=True, return_pixels=False): """ @@ -1394,10 +1643,14 @@ def valid_pixels_pos(self, lonlat=True, return_pixels=False): """ if return_pixels: valid_pixels = self.valid_pixels - lon, lat = hpg.pixel_to_angle(self.nside_sparse, valid_pixels, lonlat=lonlat) + lon, lat = hpg.pixel_to_angle( + self.nside_sparse, valid_pixels, lonlat=lonlat + ) return (valid_pixels, lon, lat) else: - return hpg.pixel_to_angle(self.nside_sparse, self.valid_pixels, lonlat=lonlat) + return hpg.pixel_to_angle( + self.nside_sparse, self.valid_pixels, lonlat=lonlat + ) @property def n_valid(self): @@ -1441,7 +1694,7 @@ def iter_valid_pixels_by_covpix(self): >>> for valid_pixels in m.iter_valid_pixels_by_covpix(): ... print(valid_pixels) """ - cov_pixels, = np.where(self._cov_map.coverage_mask) + (cov_pixels,) = np.where(self._cov_map.coverage_mask) for cov_pix in cov_pixels: yield self.valid_pixels_single_covpix(cov_pix) @@ -1467,23 +1720,30 @@ def valid_pixels_single_covpix(self, cov_pix): return np.asarray([], dtype=np.int64) # This is the start of the coverage pixel slice. - start = (self._cov_map[cov_pix] + - self._cov_map.nfine_per_cov*cov_pix) + start = self._cov_map[cov_pix] + self._cov_map.nfine_per_cov * cov_pix s = slice(start, start + self._cov_map.nfine_per_cov) if self._is_rec_array: - valid_pixel_inds, = np.where(self._sparse_map[self._primary][s] != self._sentinel) + (valid_pixel_inds,) = np.where( + self._sparse_map[self._primary][s] != self._sentinel + ) elif self._is_wide_mask: - valid_pixel_inds, = np.where(np.any(self._sparse_map[s, :] != self._sentinel, axis=1)) + (valid_pixel_inds,) = np.where( + np.any(self._sparse_map[s, :] != self._sentinel, axis=1) + ) elif self._is_bit_packed: - valid_pixel_inds, = np.where(np.asarray(self._sparse_map[s]) != self._sentinel) + (valid_pixel_inds,) = np.where( + np.asarray(self._sparse_map[s]) != self._sentinel + ) else: - valid_pixel_inds, = np.where(self._sparse_map[s] != self._sentinel) + (valid_pixel_inds,) = np.where(self._sparse_map[s] != self._sentinel) # We need to get the correct offsets for our valid pixel subset. - return (valid_pixel_inds - - self._cov_map[self._cov_map.cov_pixels_from_index(start)] + - start) + return ( + valid_pixel_inds + - self._cov_map[self._cov_map.cov_pixels_from_index(start)] + + start + ) def get_valid_area(self, degrees=True): """ @@ -1498,9 +1758,11 @@ def get_valid_area(self, degrees=True): ------- valid_area : `float` """ - return self.n_valid*hpg.nside_to_pixel_area(self._nside_sparse, degrees=degrees) + return self.n_valid * hpg.nside_to_pixel_area( + self._nside_sparse, degrees=degrees + ) - def _degrade(self, nside_out, reduction='mean', weights=None): + def _degrade(self, nside_out, reduction="mean", weights=None): """ Auxiliary method to reduce the resolution, i.e., increase the pixel size of a given sparse map (which is called by `degrade`). @@ -1521,7 +1783,9 @@ def _degrade(self, nside_out, reduction='mean', weights=None): New map, at the desired resolution. """ if self._nside_sparse < nside_out: - raise ValueError('nside_out should be smaller than nside for the sparse_map.') + raise ValueError( + "nside_out should be smaller than nside for the sparse_map." + ) # Count the number of filled pixels in the coverage mask npop_pix = np.count_nonzero(self.coverage_mask) # We need the new bit_shifts and we have to build a new CovIndexMap @@ -1531,41 +1795,56 @@ def _degrade(self, nside_out, reduction='mean', weights=None): # Check weights and add guards weight_values = None if weights is not None: - if reduction != 'wmean': - warnings.warn('Weights only used with wmean reduction. Ignoring weights.', - UserWarning) + if reduction != "wmean": + warnings.warn( + "Weights only used with wmean reduction. Ignoring weights.", + UserWarning, + ) else: # Check format/size of weight-map here. if not isinstance(weights, HealSparseMap): raise ValueError("weights must be a HealSparseMap.") - if weights.is_rec_array or weights.is_wide_mask_map or weights.is_integer_map: + if ( + weights.is_rec_array + or weights.is_wide_mask_map + or weights.is_integer_map + ): raise ValueError("weights must be a floating-point map.") - bad_map = ((weights.nside_sparse != self.nside_sparse) or - (weights.nside_coverage != self.nside_coverage) or - (not np.array_equal(weights.valid_pixels, self.valid_pixels))) + bad_map = ( + (weights.nside_sparse != self.nside_sparse) + or (weights.nside_coverage != self.nside_coverage) + or (not np.array_equal(weights.valid_pixels, self.valid_pixels)) + ) if bad_map: - raise ValueError('weights dimensions must be the same as this map.') + raise ValueError("weights dimensions must be the same as this map.") weight_values = weights._sparse_map # Set to zero weight those pixels that are not observed # This is valid for all types of maps because they share the same valid_pixels. weight_values[weight_values == weights._sentinel] = 0.0 - weight_values = weight_values.reshape((npop_pix + 1, - (nside_out//self.nside_coverage)**2, -1)) - elif reduction == 'wmean': - raise ValueError('Must specify weights when using wmean reduction.') + weight_values = weight_values.reshape( + (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) + ) + elif reduction == "wmean": + raise ValueError("Must specify weights when using wmean reduction.") # At this point, the weight map has been checked and will only be used if # the reduction is set to wmean. # Work with wide masks if self._is_wide_mask: - if reduction not in ['and', 'or']: - raise NotImplementedError('Cannot degrade a wide_mask map with this \ - reduction operation, try and/or.') + if reduction not in ["and", "or"]: + raise NotImplementedError( + "Cannot degrade a wide_mask map with this \ + reduction operation, try and/or." + ) else: nbits = self._sparse_map.shape[1] - aux = self._sparse_map.reshape((npop_pix+1, (nside_out//self.nside_coverage)**2, -1, nbits)) - sparse_map_out = reduce_array(aux, reduction=reduction, axis=2).reshape((-1, nbits)) + aux = self._sparse_map.reshape( + (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1, nbits) + ) + sparse_map_out = reduce_array(aux, reduction=reduction, axis=2).reshape( + (-1, nbits) + ) sentinel_out = self._sentinel # Work with RecArray (we have to change the resolution to all maps...) @@ -1579,11 +1858,13 @@ def _degrade(self, nside_out, reduction='mean', weights=None): else: dtype.append((key, value[0])) # Allocate new map - sparse_map_out = np.zeros((npop_pix + 1)*nfine_per_cov, dtype=dtype) + sparse_map_out = np.zeros((npop_pix + 1) * nfine_per_cov, dtype=dtype) for key, value in sparse_map_out.dtype.fields.items(): aux = self._sparse_map[key].astype(np.float64) aux[self._sparse_map[self._primary] == self._sentinel] = np.nan - aux = aux.reshape((npop_pix + 1, (nside_out//self.nside_coverage)**2, -1)) + aux = aux.reshape( + (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) + ) # Perform the reduction operation (check utils.reduce_array) aux = reduce_array(aux, reduction=reduction, weights=weight_values) # Transform back to sentinel value @@ -1591,8 +1872,12 @@ def _degrade(self, nside_out, reduction='mean', weights=None): sparse_map_out[key] = aux # Work with int array and ndarray - elif (issubclass(self._sparse_map.dtype.type, np.integer)) and (reduction in ['and', 'or']): - aux = self._sparse_map.reshape((npop_pix+1, (nside_out//self.nside_coverage)**2, -1)) + elif (issubclass(self._sparse_map.dtype.type, np.integer)) and ( + reduction in ["and", "or"] + ): + aux = self._sparse_map.reshape( + (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) + ) sparse_map_out = reduce_array(aux, reduction=reduction) sentinel_out = self._sentinel else: @@ -1603,7 +1888,9 @@ def _degrade(self, nside_out, reduction='mean', weights=None): sentinel_out = hpg.UNSEEN aux = self._sparse_map.astype(aux_dtype) aux[self._sparse_map == self._sentinel] = np.nan - aux = aux.reshape((npop_pix + 1, (nside_out//self.nside_coverage)**2, -1)) + aux = aux.reshape( + (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) + ) aux = reduce_array(aux, reduction=reduction, weights=weight_values) # NaN are converted to UNSEEN aux[np.isnan(aux)] = sentinel_out @@ -1611,13 +1898,18 @@ def _degrade(self, nside_out, reduction='mean', weights=None): # The coverage index map is now offset, we have to build a new one # Note that we need to keep the same order of the coverage map - new_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, - nside_out, - self._cov_map._block_to_cov_index) - return HealSparseMap(cov_map=new_cov_map, sparse_map=sparse_map_out, - nside_sparse=nside_out, primary=self._primary, sentinel=sentinel_out) + new_cov_map = HealSparseCoverage.make_from_pixels( + self.nside_coverage, nside_out, self._cov_map._block_to_cov_index + ) + return HealSparseMap( + cov_map=new_cov_map, + sparse_map=sparse_map_out, + nside_sparse=nside_out, + primary=self._primary, + sentinel=sentinel_out, + ) - def degrade(self, nside_out, reduction='mean', weights=None): + def degrade(self, nside_out, reduction="mean", weights=None): """ Decrease the resolution of the map, i.e., increase the pixel size. @@ -1644,41 +1936,56 @@ def degrade(self, nside_out, reduction='mean', weights=None): if self._is_bit_packed: if weights is not None: - raise NotImplementedError("Map degrading with weights is not supported for bit_packed maps.") + raise NotImplementedError( + "Map degrading with weights is not supported for bit_packed maps." + ) elif nside_out < self.nside_coverage: raise NotImplementedError( "Map degrading below nside_coverage is not supported for bit_packed maps.", ) # Do coverage-pixel by coverage-pixel: - sparse_map_out = HealSparseMap.make_empty(self.nside_coverage, nside_out, dtype=np.float64) + sparse_map_out = HealSparseMap.make_empty( + self.nside_coverage, nside_out, dtype=np.float64 + ) for covpix_map in self.get_covpix_maps(): - covpix_degrade = covpix_map.astype(np.bool_)._degrade(nside_out, reduction=reduction) - sparse_map_out[covpix_degrade.valid_pixels] = covpix_degrade[covpix_degrade.valid_pixels] + covpix_degrade = covpix_map.astype(np.bool_)._degrade( + nside_out, reduction=reduction + ) + sparse_map_out[covpix_degrade.valid_pixels] = covpix_degrade[ + covpix_degrade.valid_pixels + ] return sparse_map_out if nside_out < self.nside_coverage: # The way we do the reduction requires nside_out to be >= nside_coverage # we allocate a new map with the required nside_out # CAUTION: This may require a lot of memory!! - warnings.warn("`nside_out` < `nside_coverage`. \ + warnings.warn( + "`nside_out` < `nside_coverage`. \ Allocating new map with nside_coverage=nside_out", - ResourceWarning) - sparse_map_out = HealSparseMap.make_empty_like(self, - nside_coverage=nside_out) + ResourceWarning, + ) + sparse_map_out = HealSparseMap.make_empty_like( + self, nside_coverage=nside_out + ) if weights is not None: wgt_valid = weights.valid_pixels - _weights = HealSparseMap.make_empty_like(weights, nside_coverage=nside_out) + _weights = HealSparseMap.make_empty_like( + weights, nside_coverage=nside_out + ) _weights[wgt_valid] = weights[wgt_valid] weights = _weights valid_pixels = self.valid_pixels sparse_map_out[valid_pixels] = self[valid_pixels] - sparse_map_out = sparse_map_out._degrade(nside_out, reduction=reduction, weights=weights) + sparse_map_out = sparse_map_out._degrade( + nside_out, reduction=reduction, weights=weights + ) else: # Regular degrade - sparse_map_out = self._degrade(nside_out, - reduction=reduction, - weights=weights) + sparse_map_out = self._degrade( + nside_out, reduction=reduction, weights=weights + ) return sparse_map_out @@ -1707,18 +2014,22 @@ def upgrade(self, nside_out): raise NotImplementedError("Upgrading bit_packed maps is not yet supported.") # Make an order preserving coverage map. - new_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, - nside_out, - self._cov_map._block_to_cov_index) + new_cov_map = HealSparseCoverage.make_from_pixels( + self.nside_coverage, nside_out, self._cov_map._block_to_cov_index + ) # And a new sparse map bit_shift = _compute_bitshift(self._nside_sparse, nside_out) nout_per_self = 2**bit_shift # Nest maps at higher resolution are just repeats of the same values new_sparse_map = np.repeat(self._sparse_map, nout_per_self) - return HealSparseMap(cov_map=new_cov_map, sparse_map=new_sparse_map, - nside_sparse=nside_out, primary=self._primary, - sentinel=self._sentinel) + return HealSparseMap( + cov_map=new_cov_map, + sparse_map=new_sparse_map, + nside_sparse=nside_out, + primary=self._primary, + sentinel=self._sentinel, + ) def apply_mask(self, mask_map, mask_bits=None, mask_bit_arr=None, in_place=True): """ @@ -1757,34 +2068,48 @@ def apply_mask(self, mask_map, mask_bits=None, mask_bit_arr=None, in_place=True) if mask_bits is None: if mask_map.is_wide_mask_map: if mask_bit_arr is None: - bad_pixels, = np.where(mask_map.get_values_pix(valid_pixels).sum(axis=1) > 0) + (bad_pixels,) = np.where( + mask_map.get_values_pix(valid_pixels).sum(axis=1) > 0 + ) else: mask_values = mask_map.get_values_pix(valid_pixels) - bit_value = _bitvals_to_packed_array(mask_bit_arr, mask_map._wide_mask_maxbits) - bad_pixels, = np.where(np.any((mask_values & bit_value) > 0, axis=1)) + bit_value = _bitvals_to_packed_array( + mask_bit_arr, mask_map._wide_mask_maxbits + ) + (bad_pixels,) = np.where( + np.any((mask_values & bit_value) > 0, axis=1) + ) else: - bad_pixels, = np.where(mask_map.get_values_pix(valid_pixels) > 0) + (bad_pixels,) = np.where(mask_map.get_values_pix(valid_pixels) > 0) else: - bad_pixels, = np.where((mask_map.get_values_pix(valid_pixels) & mask_bits) > 0) + (bad_pixels,) = np.where( + (mask_map.get_values_pix(valid_pixels) & mask_bits) > 0 + ) if in_place: new_map = self else: - new_map = HealSparseMap(cov_map=self._cov_map.copy(), - sparse_map=self._sparse_map.copy(), - nside_sparse=self._nside_sparse, - primary=self._primary, - sentinel=self._sentinel) + new_map = HealSparseMap( + cov_map=self._cov_map.copy(), + sparse_map=self._sparse_map.copy(), + nside_sparse=self._nside_sparse, + primary=self._primary, + sentinel=self._sentinel, + ) new_value = new_map._sparse_map[0] ipnest_cov = self._cov_map.cov_pixels(valid_pixels[bad_pixels]) - new_map._sparse_map[valid_pixels[bad_pixels] + new_map._cov_map[ipnest_cov]] = new_value + new_map._sparse_map[valid_pixels[bad_pixels] + new_map._cov_map[ipnest_cov]] = ( + new_value + ) return new_map - def interpolate_pos(self, ra_or_theta, dec_or_phi, lonlat=True, allow_partial=False): + def interpolate_pos( + self, ra_or_theta, dec_or_phi, lonlat=True, allow_partial=False + ): """ Return the bilinear interpolation of the map using 4 nearest neighbors. @@ -1828,7 +2153,7 @@ def interpolate_pos(self, ra_or_theta, dec_or_phi, lonlat=True, allow_partial=Fa lonlat=lonlat, ) aux = self.get_values_pix(interp_pix) - out_of_bounds = (aux == self._sentinel) + out_of_bounds = aux == self._sentinel aux = aux.astype(np.float64) aux[out_of_bounds] = np.nan @@ -1841,7 +2166,7 @@ def interpolate_pos(self, ra_or_theta, dec_or_phi, lonlat=True, allow_partial=Fa interp_wgt[out_of_bounds] = np.nan wgt_sum = np.nansum(interp_wgt, axis=1) values = np.nansum(aux * interp_wgt, axis=1) - all_bad = (wgt_sum == 0.0) + all_bad = wgt_sum == 0.0 values[~all_bad] /= wgt_sum[~all_bad] # Any pixel that has all bad neighbors will be UNSEEN. values[all_bad] = hpg.UNSEEN @@ -1854,7 +2179,9 @@ def __getitem__(self, key): """ if isinstance(key, str): if not self._is_rec_array: - raise IndexError("HealSparseMap is not a recarray map, cannot use string index.") + raise IndexError( + "HealSparseMap is not a recarray map, cannot use string index." + ) return self.get_single(key, sentinel=None) elif isinstance(key, numbers.Integral): # Get a single pixel @@ -1863,7 +2190,11 @@ def __getitem__(self, key): elif isinstance(key, slice): # Get a slice of pixels start = key.start if key.start is not None else 0 - stop = key.stop if key.stop is not None else hpg.nside_to_npixel(self._nside_sparse) + stop = ( + key.stop + if key.stop is not None + else hpg.nside_to_npixel(self._nside_sparse) + ) step = key.step if key.step is not None else 1 return self.get_values_pix(np.arange(start, stop, step)) elif isinstance(key, np.ndarray): @@ -1877,11 +2208,15 @@ def __getitem__(self, key): arr = np.atleast_1d(key) if len(arr) > 0: if not is_integer_value(arr[0]): - raise IndexError("List array indices must be integers for __getitem__") + raise IndexError( + "List array indices must be integers for __getitem__" + ) return self.get_values_pix(arr) else: - raise IndexError("Illegal index type (%s) for __getitem__ in HealSparseMap." % - (key.__class__)) + raise IndexError( + "Illegal index type (%s) for __getitem__ in HealSparseMap." + % (key.__class__) + ) def __setitem__(self, key, value): """ @@ -1893,10 +2228,13 @@ def __setitem__(self, key, value): elif isinstance(key, slice): # Set a slice of pixels start = key.start if key.start is not None else 0 - stop = key.stop if key.stop is not None else hpg.nside_to_npixel(self._nside_sparse) + stop = ( + key.stop + if key.stop is not None + else hpg.nside_to_npixel(self._nside_sparse) + ) step = key.step if key.step is not None else 1 - return self.update_values_pix(np.arange(start, stop, step), - value) + return self.update_values_pix(np.arange(start, stop, step), value) elif isinstance(key, np.ndarray): test_value = np.zeros(1, key.dtype)[0] if not is_integer_value(test_value): @@ -1905,11 +2243,15 @@ def __setitem__(self, key, value): elif isinstance(key, list): arr = np.atleast_1d(key) if len(arr) > 0 and not is_integer_value(arr[0]): - raise IndexError("List/Tuple array indices must be integers for __setitem__") + raise IndexError( + "List/Tuple array indices must be integers for __setitem__" + ) return self.update_values_pix(arr, value) else: - raise IndexError("Illegal index type (%s) for __setitem__ in HealSparseMap." % - (key.__class__)) + raise IndexError( + "Illegal index type (%s) for __setitem__ in HealSparseMap." + % (key.__class__) + ) def get_single(self, key, sentinel=None, copy=False): """ @@ -1943,18 +2285,25 @@ def get_single(self, key, sentinel=None, copy=False): # on how they were constructed (though using make_empty should be safe). # However, these linked maps cannot be used to add new pixels which # is why there is the _is_view flag. - return HealSparseMap(cov_map=self._cov_map, - sparse_map=self._sparse_map[key], - nside_sparse=self._nside_sparse, sentinel=_sentinel, - _is_view=True) + return HealSparseMap( + cov_map=self._cov_map, + sparse_map=self._sparse_map[key], + nside_sparse=self._nside_sparse, + sentinel=_sentinel, + _is_view=True, + ) new_sparse_map = np.full_like(self._sparse_map[key], _sentinel) - valid_indices = (self._sparse_map[self._primary] != self._sentinel) + valid_indices = self._sparse_map[self._primary] != self._sentinel new_sparse_map[valid_indices] = self._sparse_map[key][valid_indices] - return HealSparseMap(cov_map=self._cov_map, sparse_map=new_sparse_map, - nside_sparse=self._nside_sparse, sentinel=_sentinel) + return HealSparseMap( + cov_map=self._cov_map, + sparse_map=new_sparse_map, + nside_sparse=self._nside_sparse, + sentinel=_sentinel, + ) def get_single_covpix_map(self, covpix): """ @@ -1974,38 +2323,51 @@ def get_single_covpix_map(self, covpix): """ nfine_per_cov = self._cov_map._nfine_per_cov - if self._cov_map[covpix] + covpix*nfine_per_cov < nfine_per_cov: + if self._cov_map[covpix] + covpix * nfine_per_cov < nfine_per_cov: # Pixel is not in the coverage map; return an empty map return HealSparseMap.make_empty_like(self) - new_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, - self._nside_sparse, - [covpix]) + new_cov_map = HealSparseCoverage.make_from_pixels( + self.nside_coverage, self._nside_sparse, [covpix] + ) if self._is_wide_mask: - new_sparse_map = np.zeros((2*nfine_per_cov, self._wide_mask_width), dtype=self.dtype) + new_sparse_map = np.zeros( + (2 * nfine_per_cov, self._wide_mask_width), dtype=self.dtype + ) # Copy overflow bin - new_sparse_map[0: nfine_per_cov, :] = self._sparse_map[0: nfine_per_cov, :] + new_sparse_map[0:nfine_per_cov, :] = self._sparse_map[0:nfine_per_cov, :] # Copy the pixel - new_sparse_map[nfine_per_cov: 2*nfine_per_cov, :] = self._sparse_map[ - self._cov_map[covpix] + covpix*nfine_per_cov: - self._cov_map[covpix] + covpix*nfine_per_cov + nfine_per_cov, :] + new_sparse_map[nfine_per_cov : 2 * nfine_per_cov, :] = self._sparse_map[ + self._cov_map[covpix] + covpix * nfine_per_cov : self._cov_map[covpix] + + covpix * nfine_per_cov + + nfine_per_cov, + :, + ] elif self._is_bit_packed: - new_sparse_map = _PackedBoolArray(size=nfine_per_cov*2) - new_sparse_map[nfine_per_cov: 2*nfine_per_cov] = self._sparse_map[ - self._cov_map[covpix] + covpix*nfine_per_cov: - self._cov_map[covpix] + covpix*nfine_per_cov + nfine_per_cov] + new_sparse_map = _PackedBoolArray(size=nfine_per_cov * 2) + new_sparse_map[nfine_per_cov : 2 * nfine_per_cov] = self._sparse_map[ + self._cov_map[covpix] + covpix * nfine_per_cov : self._cov_map[covpix] + + covpix * nfine_per_cov + + nfine_per_cov + ] else: - new_sparse_map = np.zeros(2*nfine_per_cov, dtype=self.dtype) + new_sparse_map = np.zeros(2 * nfine_per_cov, dtype=self.dtype) # Copy overflow bin - new_sparse_map[0: nfine_per_cov] = self._sparse_map[0: nfine_per_cov] + new_sparse_map[0:nfine_per_cov] = self._sparse_map[0:nfine_per_cov] # Copy the pixel - new_sparse_map[nfine_per_cov: 2*nfine_per_cov] = self._sparse_map[ - self._cov_map[covpix] + covpix*nfine_per_cov: - self._cov_map[covpix] + covpix*nfine_per_cov + nfine_per_cov] + new_sparse_map[nfine_per_cov : 2 * nfine_per_cov] = self._sparse_map[ + self._cov_map[covpix] + covpix * nfine_per_cov : self._cov_map[covpix] + + covpix * nfine_per_cov + + nfine_per_cov + ] - return HealSparseMap(cov_map=new_cov_map, sparse_map=new_sparse_map, - nside_sparse=self._nside_sparse, primary=self._primary, - sentinel=self._sentinel) + return HealSparseMap( + cov_map=new_cov_map, + sparse_map=new_sparse_map, + nside_sparse=self._nside_sparse, + primary=self._primary, + sentinel=self._sentinel, + ) def get_covpix_maps(self): """ @@ -2024,7 +2386,7 @@ def get_covpix_maps(self): >>> for covpix_map in m.get_covpix_maps(): ... print(covpix_map.valid_pixels) """ - cov_pixels, = np.where(self._cov_map.coverage_mask) + (cov_pixels,) = np.where(self._cov_map.coverage_mask) for cov_pix in cov_pixels: yield self.get_single_covpix_map(cov_pix) @@ -2061,14 +2423,18 @@ def astype(self, dtype, sentinel=None): else: new_sparse_map = np.zeros(self._sparse_map.shape, dtype=dtype) - valid_pix = (self._sparse_map != self._sentinel) + valid_pix = self._sparse_map != self._sentinel new_sparse_map[valid_pix] = self._sparse_map[valid_pix].astype(dtype) _sentinel = check_sentinel(new_sparse_map.dtype.type, sentinel) new_sparse_map[~valid_pix] = _sentinel - return HealSparseMap(cov_map=self._cov_map, sparse_map=new_sparse_map, - nside_sparse=self.nside_sparse, sentinel=_sentinel) + return HealSparseMap( + cov_map=self._cov_map, + sparse_map=new_sparse_map, + nside_sparse=self.nside_sparse, + sentinel=_sentinel, + ) def as_bit_packed_map(self): """ @@ -2085,29 +2451,39 @@ def as_bit_packed_map(self): return self.copy() if (self._cov_map.nfine_per_cov % 8) != 0: - raise ValueError("Can only create a bit_packed mask map at least two " - "healpix levels between coverage and mask.") + raise ValueError( + "Can only create a bit_packed mask map at least two " + "healpix levels between coverage and mask." + ) # Need to go through coverage pixels, and copy the data into the new thing. # There is some fancy indexing that has to happen here. # The size will be the number of coverage pixels + 1 times nside - coverage_pixels, = np.where(self.coverage_mask) + (coverage_pixels,) = np.where(self.coverage_mask) n_cov = len(coverage_pixels) - bitmask_map = _PackedBoolArray(size=(n_cov + 1)*self._cov_map.nfine_per_cov) + bitmask_map = _PackedBoolArray(size=(n_cov + 1) * self._cov_map.nfine_per_cov) # This is the map without the offset. - cov_index_map_temp = self._cov_map[:] + np.arange(hpg.nside_to_npixel(self._cov_map.nside_coverage), - dtype=np.int64)*self._cov_map.nfine_per_cov + cov_index_map_temp = ( + self._cov_map[:] + + np.arange( + hpg.nside_to_npixel(self._cov_map.nside_coverage), dtype=np.int64 + ) + * self._cov_map.nfine_per_cov + ) for cov_pix in coverage_pixels: - s = slice(cov_index_map_temp[cov_pix], cov_index_map_temp[cov_pix] + self._cov_map.nfine_per_cov) + s = slice( + cov_index_map_temp[cov_pix], + cov_index_map_temp[cov_pix] + self._cov_map.nfine_per_cov, + ) if self._is_rec_array: - bool_data = (self._sparse_map[self._primary][s] != self._sentinel) + bool_data = self._sparse_map[self._primary][s] != self._sentinel elif self._is_wide_mask: bool_data = np.any(self._sparse_map[s] != self._sentinel, axis=1) else: - bool_data = (self._sparse_map[s] != self._sentinel) + bool_data = self._sparse_map[s] != self._sentinel # This is a bulk setter for aligned data. bitmask_map[s] = bool_data @@ -2268,7 +2644,9 @@ def __iand__(self, other): elif self.dtype == np.bool_: return self._apply_boolean_map_operation(other, "and", in_place=True) else: - return self._apply_operation(other, np.bitwise_and, int_only=True, in_place=True) + return self._apply_operation( + other, np.bitwise_and, int_only=True, in_place=True + ) def __xor__(self, other): """ @@ -2290,7 +2668,9 @@ def __ixor__(self, other): if self.dtype == np.bool_: return self._apply_boolean_map_operation(other, "xor", in_place=True) else: - return self._apply_operation(other, np.bitwise_xor, int_only=True, in_place=True) + return self._apply_operation( + other, np.bitwise_xor, int_only=True, in_place=True + ) def __or__(self, other): """ @@ -2335,18 +2715,21 @@ def __ior__(self, other): elif self.dtype == np.bool_: return self._apply_boolean_map_operation(other, "or", in_place=True) else: - return self._apply_operation(other, np.bitwise_or, int_only=True, in_place=True) + return self._apply_operation( + other, np.bitwise_or, int_only=True, in_place=True + ) def invert(self): - """Perform a bitwise inversion, over the coverage pixels, in place. - """ + """Perform a bitwise inversion, over the coverage pixels, in place.""" if self.dtype != np.bool_: raise NotImplementedError("Can only use invert(~) on boolean maps.") # We invalidate the n_valid cache here. self._n_valid = None - self._sparse_map[self._cov_map.nfine_per_cov:] = ~self._sparse_map[self._cov_map.nfine_per_cov:] + self._sparse_map[self._cov_map.nfine_per_cov :] = ~self._sparse_map[ + self._cov_map.nfine_per_cov : + ] return self def __invert__(self): @@ -2359,7 +2742,9 @@ def __invert__(self): raise NotImplementedError("Can only use invert(~) on boolean maps.") sparse_map_temp = self._sparse_map.copy() - sparse_map_temp[self._cov_map.nfine_per_cov:] = ~sparse_map_temp[self._cov_map.nfine_per_cov:] + sparse_map_temp[self._cov_map.nfine_per_cov :] = ~sparse_map_temp[ + self._cov_map.nfine_per_cov : + ] return HealSparseMap( cov_map=self._cov_map.copy(), sparse_map=sparse_map_temp, @@ -2417,32 +2802,46 @@ def _apply_operation(self, other, func, int_only=False, in_place=False): other_float = True elif isinstance(other, (tuple, list)): if not self._is_wide_mask: - raise NotImplementedError("Must use a wide mask to operate with a bit list") + raise NotImplementedError( + "Must use a wide mask to operate with a bit list" + ) other_bits = True for elt in other: if not isinstance(elt, numbers.Integral): - raise NotImplementedError("Can only use an integer list of bits " - "with %s operation" % (name)) + raise NotImplementedError( + "Can only use an integer list of bits " + "with %s operation" % (name) + ) if np.max(other) >= self._wide_mask_maxbits: - raise ValueError("Bit position %d too large (>= %d)" % (np.max(other), - self._wide_mask_maxbits)) + raise ValueError( + "Bit position %d too large (>= %d)" + % (np.max(other), self._wide_mask_maxbits) + ) if self._is_wide_mask: if not other_bits: - raise NotImplementedError("Must use a bit list with the %s operation with " - "a wide mask" % (name)) + raise NotImplementedError( + "Must use a bit list with the %s operation with " + "a wide mask" % (name) + ) else: if not other_int and not other_float: - raise NotImplementedError("Can only use a constant with the %s operation" % (name)) + raise NotImplementedError( + "Can only use a constant with the %s operation" % (name) + ) if not other_int and int_only: - raise NotImplementedError("Can only use an integer constant with the %s operation" % (name)) + raise NotImplementedError( + "Can only use an integer constant with the %s operation" % (name) + ) if self._is_wide_mask: - valid_sparse_pixels = (self._sparse_map != self._sentinel).sum(axis=1, dtype=np.bool_) + valid_sparse_pixels = (self._sparse_map != self._sentinel).sum( + axis=1, dtype=np.bool_ + ) other_value = _bitvals_to_packed_array(other, self._wide_mask_maxbits) else: - valid_sparse_pixels = (self._sparse_map != self._sentinel) + valid_sparse_pixels = self._sparse_map != self._sentinel if in_place: if self._is_wide_mask: @@ -2450,7 +2849,12 @@ def _apply_operation(self, other, func, int_only=False, in_place=False): col = self._sparse_map[:, i] func(col, other_value[i], out=col, where=valid_sparse_pixels) else: - func(self._sparse_map, other, out=self._sparse_map, where=valid_sparse_pixels) + func( + self._sparse_map, + other, + out=self._sparse_map, + where=valid_sparse_pixels, + ) return self else: combinedSparseMap = self._sparse_map.copy() @@ -2459,9 +2863,18 @@ def _apply_operation(self, other, func, int_only=False, in_place=False): col = combinedSparseMap[:, i] func(col, other_value[i], out=col, where=valid_sparse_pixels) else: - func(combinedSparseMap, other, out=combinedSparseMap, where=valid_sparse_pixels) - return HealSparseMap(cov_map=self._cov_map, sparse_map=combinedSparseMap, - nside_sparse=self._nside_sparse, sentinel=self._sentinel) + func( + combinedSparseMap, + other, + out=combinedSparseMap, + where=valid_sparse_pixels, + ) + return HealSparseMap( + cov_map=self._cov_map, + sparse_map=combinedSparseMap, + nside_sparse=self._nside_sparse, + sentinel=self._sentinel, + ) def _apply_boolean_map_operation(self, other, name, in_place=False): """Apply an operation to a boolean mask map. @@ -2481,7 +2894,9 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): Resulting map """ if name not in ("and", "or", "xor"): - raise NotImplementedError("_apply_boolean_map_operation does not support %s" % (name)) + raise NotImplementedError( + "_apply_boolean_map_operation does not support %s" % (name) + ) if in_place: # We invalidate the n_valid cache here. @@ -2508,48 +2923,66 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): elif isinstance(other, HealSparseMap): # Do an operation if these are allowed if not other.dtype == np.bool_: - raise NotImplementedError("Can only combine a boolean map with another boolean map.") + raise NotImplementedError( + "Can only combine a boolean map with another boolean map." + ) if self.nside_sparse != other.nside_sparse: - raise NotImplementedError("Boolean map operations only supported between maps with the " - "same nside_sparse.") + raise NotImplementedError( + "Boolean map operations only supported between maps with the " + "same nside_sparse." + ) if self.nside_coverage != other.nside_coverage: - raise NotImplementedError("Boolean map operations only supported between maps with the " - "same nside_coverage.") + raise NotImplementedError( + "Boolean map operations only supported between maps with the " + "same nside_coverage." + ) if self.sentinel or other.sentinel: - raise NotImplementedError("Boolean map operations only supported for maps with " - "False sentinel.") + raise NotImplementedError( + "Boolean map operations only supported for maps with " + "False sentinel." + ) # This routine will combine the coverage maps of the two masks. # We then loop over coverage pixels in the other map to do the # operation. coverage_mask = self.coverage_mask | other.coverage_mask - cov_pixels_combined, = coverage_mask.nonzero() - cov_pixels_run, = other.coverage_mask.nonzero() + (cov_pixels_combined,) = coverage_mask.nonzero() + (cov_pixels_run,) = other.coverage_mask.nonzero() - new_cov_pix, = (coverage_mask & ~self.coverage_mask).nonzero() + (new_cov_pix,) = (coverage_mask & ~self.coverage_mask).nonzero() if in_place: - new_cov_pix, = (coverage_mask & ~self.coverage_mask).nonzero() + (new_cov_pix,) = (coverage_mask & ~self.coverage_mask).nonzero() self._reserve_cov_pix(new_cov_pix) cov_map_temp = self._cov_map sparse_map_temp = self._sparse_map else: # Extend the coverage pixel map and copy data into new buffer. - cov_map_temp = self._cov_map.append_pixels(len(self._sparse_map), new_cov_pix, check=False) - nsparse = (cov_pixels_combined.size + 1)*cov_map_temp.nfine_per_cov + cov_map_temp = self._cov_map.append_pixels( + len(self._sparse_map), new_cov_pix, check=False + ) + nsparse = (cov_pixels_combined.size + 1) * cov_map_temp.nfine_per_cov if self._is_bit_packed: sparse_map_temp = _PackedBoolArray(size=nsparse) else: sparse_map_temp = np.zeros(nsparse, dtype=np.bool_) - sparse_map_temp[0: len(self._sparse_map)] = self._sparse_map[0: len(self._sparse_map)] + sparse_map_temp[0 : len(self._sparse_map)] = self._sparse_map[ + 0 : len(self._sparse_map) + ] for cov_pixel in cov_pixels_run: - start_self = self._cov_map[cov_pixel] + cov_pixel*cov_map_temp.nfine_per_cov + start_self = ( + self._cov_map[cov_pixel] + cov_pixel * cov_map_temp.nfine_per_cov + ) end_self = start_self + cov_map_temp.nfine_per_cov - start_other = other._cov_map[cov_pixel] + cov_pixel*cov_map_temp.nfine_per_cov + start_other = ( + other._cov_map[cov_pixel] + cov_pixel * cov_map_temp.nfine_per_cov + ) end_other = start_other + cov_map_temp.nfine_per_cov - start_temp = cov_map_temp[cov_pixel] + cov_pixel*cov_map_temp.nfine_per_cov + start_temp = ( + cov_map_temp[cov_pixel] + cov_pixel * cov_map_temp.nfine_per_cov + ) end_temp = start_temp + cov_map_temp.nfine_per_cov # The LHS will be guaranteed to have coverage (from above), @@ -2557,18 +2990,22 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): if not in_place: # Need to copy data. - sparse_map_temp[start_temp: end_temp] = self._sparse_map[start_self: end_self] + sparse_map_temp[start_temp:end_temp] = self._sparse_map[ + start_self:end_self + ] - lhs = sparse_map_temp[start_temp: end_temp] + lhs = sparse_map_temp[start_temp:end_temp] if self._is_bit_packed == other._is_bit_packed: # These match, no conversions necessary. - rhs = other._sparse_map[start_other: end_other] + rhs = other._sparse_map[start_other:end_other] elif self._is_bit_packed and not other._is_bit_packed: # Convert the RHS to a _PackedBoolArray. - rhs = _PackedBoolArray.from_boolean_array(other._sparse_map[start_other: end_other]) + rhs = _PackedBoolArray.from_boolean_array( + other._sparse_map[start_other:end_other] + ) elif not self._is_bit_packed and other._is_bit_packed: # Expand the RHS to a regular boolean array. - rhs = np.asarray(other._sparse_map[start_other: end_other]) + rhs = np.asarray(other._sparse_map[start_other:end_other]) if name == "and": lhs &= rhs @@ -2577,18 +3014,28 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): elif name == "xor": lhs ^= rhs else: - raise NotImplementedError("Can only use a boolean or a boolean map with operation %s" % (name)) + raise NotImplementedError( + "Can only use a boolean or a boolean map with operation %s" % (name) + ) if in_place: return self else: - return HealSparseMap(cov_map=cov_map_temp, sparse_map=sparse_map_temp, - nside_sparse=self._nside_sparse, sentinel=self._sentinel) + return HealSparseMap( + cov_map=cov_map_temp, + sparse_map=sparse_map_temp, + nside_sparse=self._nside_sparse, + sentinel=self._sentinel, + ) def __copy__(self): - return HealSparseMap(cov_map=self._cov_map.copy(), - sparse_map=self._sparse_map.copy(), nside_sparse=self._nside_sparse, - sentinel=self._sentinel, primary=self._primary) + return HealSparseMap( + cov_map=self._cov_map.copy(), + sparse_map=self._sparse_map.copy(), + nside_sparse=self._nside_sparse, + sentinel=self._sentinel, + primary=self._primary, + ) def copy(self): return self.__copy__() @@ -2597,17 +3044,19 @@ def __repr__(self): return self.__str__() def __str__(self): - descr = 'HealSparseMap: nside_coverage = %d, nside_sparse = %d' % (self.nside_coverage, - self._nside_sparse) + descr = "HealSparseMap: nside_coverage = %d, nside_sparse = %d" % ( + self.nside_coverage, + self._nside_sparse, + ) if self._is_rec_array: - descr += ', record array type.\n' + descr += ", record array type.\n" descr += self._sparse_map.dtype.descr.__str__() elif self._is_wide_mask: - descr += ', %d bit wide mask' % (self._wide_mask_maxbits) + descr += ", %d bit wide mask" % (self._wide_mask_maxbits) elif self._is_bit_packed: - descr += ', boolean bit-packed mask' + descr += ", boolean bit-packed mask" else: - descr += ', ' + self._sparse_map.dtype.name + descr += ", " + self._sparse_map.dtype.name add_n_valid = True if self._is_bit_packed and self._n_valid is None: @@ -2617,6 +3066,6 @@ def __str__(self): add_n_valid = False if add_n_valid: - descr += ', %d valid pixels' % (self.n_valid) + descr += ", %d valid pixels" % (self.n_valid) return descr From ee0191874f9bbf5386af2e18d3362ae3270ef748 Mon Sep 17 00:00:00 2001 From: Patrick Wells Date: Mon, 27 Apr 2026 10:59:39 -0500 Subject: [PATCH 2/6] staticmethod -> classmethod, formatting --- healsparse/healSparseMap.py | 80 ++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/healsparse/healSparseMap.py b/healsparse/healSparseMap.py index a43d302..77c4294 100644 --- a/healsparse/healSparseMap.py +++ b/healsparse/healSparseMap.py @@ -416,7 +416,7 @@ def make_empty_like( bit_packed=bit_packed, ) - @staticmethod + @classmethod def from_data( pixels, data, nside_coverage, nside_sparse, nest=True, sentinel=hpg.UNSEEN ): @@ -932,8 +932,8 @@ def _update_values_pixel_ranges(self, pixel_ranges, value, operation, no_append) delta_covpix = cov_pix_ranges[:, 1] - cov_pix_ranges[:, 0] covpix_start_values = ( - self._cov_map[cov_pix_ranges.ravel()] - + self._cov_map.nfine_per_cov * cov_pix_ranges.ravel() + self._cov_map[cov_pix_ranges.ravel()] + + self._cov_map.nfine_per_cov * cov_pix_ranges.ravel() ).reshape(cov_pix_ranges.shape) covpix_offset_values = self._cov_map[ @@ -982,8 +982,8 @@ def _do_operation_on_sparse_map_range( # Nothing to set here. continue start = ( - self._cov_map[cov_pix_full] - + self._cov_map.nfine_per_cov * cov_pix_full + self._cov_map[cov_pix_full] + + self._cov_map.nfine_per_cov * cov_pix_full ) stop = start + self._cov_map.nfine_per_cov _do_operation_on_sparse_map_range( @@ -996,8 +996,8 @@ def _do_operation_on_sparse_map_range( else: # The final coverage pixel will be partly covered. start = ( - self._cov_map[cov_pix_ranges[i, 1]] - + self._cov_map.nfine_per_cov * (cov_pix_ranges[i, 1]) + self._cov_map[cov_pix_ranges[i, 1]] + + self._cov_map.nfine_per_cov * (cov_pix_ranges[i, 1]) ) stop = pixel_ranges[i, 1] + covpix_offset_values[i, 1] _do_operation_on_sparse_map_range( @@ -1616,8 +1616,8 @@ def valid_pixels(self): (valid_pixel_inds,) = np.where(self._sparse_map != self._sentinel) return ( - valid_pixel_inds - - self._cov_map[self._cov_map.cov_pixels_from_index(valid_pixel_inds)] + valid_pixel_inds - + self._cov_map[self._cov_map.cov_pixels_from_index(valid_pixel_inds)] ) def valid_pixels_pos(self, lonlat=True, return_pixels=False): @@ -1740,9 +1740,9 @@ def valid_pixels_single_covpix(self, cov_pix): # We need to get the correct offsets for our valid pixel subset. return ( - valid_pixel_inds - - self._cov_map[self._cov_map.cov_pixels_from_index(start)] - + start + valid_pixel_inds - + self._cov_map[self._cov_map.cov_pixels_from_index(start)] + + start ) def get_valid_area(self, degrees=True): @@ -1805,15 +1805,15 @@ def _degrade(self, nside_out, reduction="mean", weights=None): if not isinstance(weights, HealSparseMap): raise ValueError("weights must be a HealSparseMap.") if ( - weights.is_rec_array - or weights.is_wide_mask_map - or weights.is_integer_map + weights.is_rec_array or + weights.is_wide_mask_map or + weights.is_integer_map ): raise ValueError("weights must be a floating-point map.") bad_map = ( - (weights.nside_sparse != self.nside_sparse) - or (weights.nside_coverage != self.nside_coverage) - or (not np.array_equal(weights.valid_pixels, self.valid_pixels)) + (weights.nside_sparse != self.nside_sparse) or + (weights.nside_coverage != self.nside_coverage) or + (not np.array_equal(weights.valid_pixels, self.valid_pixels)) ) if bad_map: raise ValueError("weights dimensions must be the same as this map.") @@ -2337,28 +2337,28 @@ def get_single_covpix_map(self, covpix): # Copy overflow bin new_sparse_map[0:nfine_per_cov, :] = self._sparse_map[0:nfine_per_cov, :] # Copy the pixel - new_sparse_map[nfine_per_cov : 2 * nfine_per_cov, :] = self._sparse_map[ - self._cov_map[covpix] + covpix * nfine_per_cov : self._cov_map[covpix] - + covpix * nfine_per_cov - + nfine_per_cov, + new_sparse_map[nfine_per_cov: 2 * nfine_per_cov, :] = self._sparse_map[ + self._cov_map[covpix] + covpix * nfine_per_cov: self._cov_map[covpix] + + covpix * nfine_per_cov + + nfine_per_cov, :, ] elif self._is_bit_packed: new_sparse_map = _PackedBoolArray(size=nfine_per_cov * 2) - new_sparse_map[nfine_per_cov : 2 * nfine_per_cov] = self._sparse_map[ - self._cov_map[covpix] + covpix * nfine_per_cov : self._cov_map[covpix] - + covpix * nfine_per_cov - + nfine_per_cov + new_sparse_map[nfine_per_cov: 2 * nfine_per_cov] = self._sparse_map[ + self._cov_map[covpix] + covpix * nfine_per_cov: self._cov_map[covpix] + + covpix * nfine_per_cov + + nfine_per_cov ] else: new_sparse_map = np.zeros(2 * nfine_per_cov, dtype=self.dtype) # Copy overflow bin new_sparse_map[0:nfine_per_cov] = self._sparse_map[0:nfine_per_cov] # Copy the pixel - new_sparse_map[nfine_per_cov : 2 * nfine_per_cov] = self._sparse_map[ - self._cov_map[covpix] + covpix * nfine_per_cov : self._cov_map[covpix] - + covpix * nfine_per_cov - + nfine_per_cov + new_sparse_map[nfine_per_cov: 2 * nfine_per_cov] = self._sparse_map[ + self._cov_map[covpix] + covpix * nfine_per_cov: self._cov_map[covpix] + + covpix * nfine_per_cov + + nfine_per_cov ] return HealSparseMap( @@ -2466,11 +2466,11 @@ def as_bit_packed_map(self): # This is the map without the offset. cov_index_map_temp = ( - self._cov_map[:] - + np.arange( + self._cov_map[:] + + np.arange( hpg.nside_to_npixel(self._cov_map.nside_coverage), dtype=np.int64 - ) - * self._cov_map.nfine_per_cov + ) * + self._cov_map.nfine_per_cov ) for cov_pix in coverage_pixels: @@ -2727,8 +2727,8 @@ def invert(self): # We invalidate the n_valid cache here. self._n_valid = None - self._sparse_map[self._cov_map.nfine_per_cov :] = ~self._sparse_map[ - self._cov_map.nfine_per_cov : + self._sparse_map[self._cov_map.nfine_per_cov:] = ~self._sparse_map[ + self._cov_map.nfine_per_cov: ] return self @@ -2742,8 +2742,8 @@ def __invert__(self): raise NotImplementedError("Can only use invert(~) on boolean maps.") sparse_map_temp = self._sparse_map.copy() - sparse_map_temp[self._cov_map.nfine_per_cov :] = ~sparse_map_temp[ - self._cov_map.nfine_per_cov : + sparse_map_temp[self._cov_map.nfine_per_cov:] = ~sparse_map_temp[ + self._cov_map.nfine_per_cov: ] return HealSparseMap( cov_map=self._cov_map.copy(), @@ -2967,8 +2967,8 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): else: sparse_map_temp = np.zeros(nsparse, dtype=np.bool_) - sparse_map_temp[0 : len(self._sparse_map)] = self._sparse_map[ - 0 : len(self._sparse_map) + sparse_map_temp[0: len(self._sparse_map)] = self._sparse_map[ + 0: len(self._sparse_map) ] for cov_pixel in cov_pixels_run: From 304dc9e40603bc73f97edf0d9600511d2198dbf3 Mon Sep 17 00:00:00 2001 From: Patrick Wells Date: Tue, 28 Apr 2026 10:15:53 -0500 Subject: [PATCH 3/6] Remove formatting --- healsparse/healSparseMap.py | 1101 +++++++++++------------------------ 1 file changed, 350 insertions(+), 751 deletions(-) diff --git a/healsparse/healSparseMap.py b/healsparse/healSparseMap.py index 77c4294..c8a2490 100644 --- a/healsparse/healSparseMap.py +++ b/healsparse/healSparseMap.py @@ -1,25 +1,16 @@ -import numbers -import warnings - -import hpgeom as hpg import numpy as np +import hpgeom as hpg +import numbers -from .geom import GeomBase from .healSparseCoverage import HealSparseCoverage +from .utils import reduce_array, check_sentinel, _bitvals_to_packed_array +from .utils import WIDE_NBIT, WIDE_MASK, PIXEL_RANGE_THRESHOLD +from .utils import is_integer_value, _compute_bitshift from .io_map import _read_map, _write_map, _write_moc from .packedBoolArray import _PackedBoolArray -from .utils import ( - PIXEL_RANGE_THRESHOLD, - WIDE_MASK, - WIDE_NBIT, - _bitvals_to_packed_array, - _compute_bitshift, - check_sentinel, - fast_unique, - has_duplicates, - is_integer_value, - reduce_array, -) +from .utils import fast_unique, has_duplicates, +from .geom import GeomBase +import warnings class HealSparseMap(object): @@ -27,20 +18,9 @@ class HealSparseMap(object): Class to define a HealSparseMap """ - def __init__( - self, - cov_map=None, - cov_index_map=None, - sparse_map=None, - nside_sparse=None, - healpix_map=None, - nside_coverage=None, - primary=None, - sentinel=None, - nest=True, - metadata=None, - _is_view=False, - ): + def __init__(self, cov_map=None, cov_index_map=None, sparse_map=None, nside_sparse=None, + healpix_map=None, nside_coverage=None, primary=None, sentinel=None, + nest=True, metadata=None, _is_view=False): """ Instantiate a HealSparseMap. @@ -80,7 +60,7 @@ def __init__( healSparseMap : `HealSparseMap` """ if cov_index_map is not None and cov_map is not None: - raise RuntimeError("Cannot specify both cov_index_map and cov_map") + raise RuntimeError('Cannot specify both cov_index_map and cov_map') if cov_index_map is not None: warnings.warn("cov_index_map deprecated", DeprecationWarning, stacklevel=2) cov_map = HealSparseCoverage(cov_index_map, nside_sparse) @@ -94,22 +74,17 @@ def __init__( if sentinel is None: sentinel = hpg.UNSEEN if is_integer_value(healpix_map[0]) and not is_integer_value(sentinel): - raise ValueError( - "The sentinel must be set to an integer value with an integer healpix_map" - ) + raise ValueError("The sentinel must be set to an integer value with an integer healpix_map") elif not is_integer_value(healpix_map[0]) and is_integer_value(sentinel): - raise ValueError( - "The sentinel must be set to an float value with an float healpix_map" - ) + raise ValueError("The sentinel must be set to an float value with an float healpix_map") - self._cov_map, self._sparse_map = self.convert_healpix_map( - healpix_map, nside_coverage=nside_coverage, nest=nest, sentinel=sentinel - ) + self._cov_map, self._sparse_map = self.convert_healpix_map(healpix_map, + nside_coverage=nside_coverage, + nest=nest, + sentinel=sentinel) nside_sparse = hpg.npixel_to_nside(healpix_map.size) else: - raise RuntimeError( - "Must specify either cov_map/sparse_map or healpix_map/nside_coverage" - ) + raise RuntimeError("Must specify either cov_map/sparse_map or healpix_map/nside_coverage") self._nside_sparse = nside_sparse @@ -124,47 +99,28 @@ def __init__( if self._sparse_map.dtype.fields is not None: self._is_rec_array = True if self._primary is None: - raise RuntimeError( - "Must specify `primary` field when using a recarray for the sparse_map." - ) + raise RuntimeError("Must specify `primary` field when using a recarray for the sparse_map.") - self._sentinel = check_sentinel( - self._sparse_map[self._primary].dtype.type, sentinel - ) + self._sentinel = check_sentinel(self._sparse_map[self._primary].dtype.type, sentinel) else: - if (self._sparse_map.dtype.type == WIDE_MASK) and len( - self._sparse_map.shape - ) == 2: + if ((self._sparse_map.dtype.type == WIDE_MASK) and len(self._sparse_map.shape) == 2): self._is_wide_mask = True self._wide_mask_width = self._sparse_map.shape[1] self._wide_mask_maxbits = WIDE_NBIT * self._wide_mask_width elif isinstance(self._sparse_map, _PackedBoolArray): self._is_bit_packed = True if sentinel is not False: - raise NotImplementedError( - "Can only use False sentinel for bit_packed maps." - ) + raise NotImplementedError("Can only use False sentinel for bit_packed maps.") if (self._cov_map.nfine_per_cov % 8) != 0: - raise ValueError( - "Can only create a bit_packed map at least two " - "healpix levels between coverage and mask." - ) + raise ValueError("Can only create a bit_packed map at least two " + "healpix levels between coverage and mask.") self._sentinel = check_sentinel(self._sparse_map.dtype.type, sentinel) @classmethod - def read( - cls, - filename, - nside_coverage=None, - pixels=None, - header=False, - degrade_nside=None, - weightfile=None, - reduction="mean", - use_threads=False, - hdf5_group="map", - ): + def read(cls, filename, nside_coverage=None, pixels=None, header=False, + degrade_nside=None, weightfile=None, reduction='mean', + use_threads=False, hdf5_group='map'): """ Read in a HealSparseMap. @@ -204,32 +160,14 @@ def read( header : `fitsio.FITSHDR` or `astropy.io.fits` (if header=True) Fits header for the map file. """ - return _read_map( - cls, - filename, - nside_coverage=nside_coverage, - pixels=pixels, - header=header, - degrade_nside=degrade_nside, - weightfile=weightfile, - reduction=reduction, - use_threads=use_threads, - hdf5_group=hdf5_group, - ) + return _read_map(cls, filename, nside_coverage=nside_coverage, pixels=pixels, + header=header, degrade_nside=degrade_nside, + weightfile=weightfile, reduction=reduction, use_threads=use_threads, + hdf5_group=hdf5_group) @classmethod - def make_empty( - cls, - nside_coverage, - nside_sparse, - dtype, - primary=None, - sentinel=None, - wide_mask_maxbits=None, - metadata=None, - cov_pixels=None, - bit_packed=False, - ): + def make_empty(cls, nside_coverage, nside_sparse, dtype, primary=None, sentinel=None, + wide_mask_maxbits=None, metadata=None, cov_pixels=None, bit_packed=False): """ Make an empty map with nothing in it. @@ -264,9 +202,7 @@ def make_empty( if wide_mask_maxbits is not None: if test_arr.dtype != WIDE_MASK: - raise ValueError( - "Must use dtype=healsparse.WIDE_MASK to use a wide_mask" - ) + raise ValueError("Must use dtype=healsparse.WIDE_MASK to use a wide_mask") if sentinel is not None: if sentinel != 0: raise ValueError("Sentinel must be 0 for wide_mask") @@ -281,40 +217,31 @@ def make_empty( npix = 1 else: cov_pixels = np.atleast_1d(cov_pixels) - cov_map = HealSparseCoverage.make_from_pixels( - nside_coverage, nside_sparse, cov_pixels - ) + cov_map = HealSparseCoverage.make_from_pixels(nside_coverage, nside_sparse, + cov_pixels) # We need to allocate the overflow pixel npix = cov_pixels.size + 1 if wide_mask_maxbits is not None: # The sentinel is always zero _sentinel = 0 - sparse_map = np.zeros( - (cov_map.nfine_per_cov * npix, nbitfields), dtype=dtype - ) + sparse_map = np.zeros((cov_map.nfine_per_cov*npix, nbitfields), dtype=dtype) elif bit_packed: _sentinel = check_sentinel(test_arr.dtype.type, sentinel) if (cov_map.nfine_per_cov % 8) != 0: - raise ValueError( - "Can only create a bit_packed mask at least two " - "healpix levels between coverage and mask." - ) + raise ValueError("Can only create a bit_packed mask at least two " + "healpix levels between coverage and mask.") if _sentinel: - raise NotImplementedError( - "Can only create a bit_packed map with False sentinel value." - ) - sparse_map = _PackedBoolArray(size=cov_map.nfine_per_cov * npix) + raise NotImplementedError("Can only create a bit_packed map with False sentinel value.") + sparse_map = _PackedBoolArray(size=cov_map.nfine_per_cov*npix) elif test_arr.dtype.fields is None: # Non-recarray _sentinel = check_sentinel(test_arr.dtype.type, sentinel) - sparse_map = np.full(cov_map.nfine_per_cov * npix, _sentinel, dtype=dtype) + sparse_map = np.full(cov_map.nfine_per_cov*npix, _sentinel, dtype=dtype) else: # Recarray type if primary is None: - raise RuntimeError( - "Must specify 'primary' field when using a recarray for the sparse_map." - ) + raise RuntimeError("Must specify 'primary' field when using a recarray for the sparse_map.") primary_found = False for name in test_arr.dtype.names: @@ -326,35 +253,18 @@ def make_empty( test_arr[name] = check_sentinel(test_arr[name].dtype.type, None) if not primary_found: - raise RuntimeError( - "Primary field not found in input dtype of recarray." - ) + raise RuntimeError("Primary field not found in input dtype of recarray.") - sparse_map = np.full(cov_map.nfine_per_cov * npix, test_arr, dtype=dtype) + sparse_map = np.full(cov_map.nfine_per_cov*npix, test_arr, dtype=dtype) - return cls( - cov_map=cov_map, - sparse_map=sparse_map, - nside_sparse=nside_sparse, - primary=primary, - sentinel=_sentinel, - metadata=metadata, - ) + return cls(cov_map=cov_map, sparse_map=sparse_map, + nside_sparse=nside_sparse, primary=primary, sentinel=_sentinel, + metadata=metadata) @classmethod - def make_empty_like( - cls, - sparsemap, - nside_coverage=None, - nside_sparse=None, - dtype=None, - primary=None, - sentinel=None, - wide_mask_maxbits=None, - metadata=None, - cov_pixels=None, - bit_packed=False, - ): + def make_empty_like(cls, sparsemap, nside_coverage=None, nside_sparse=None, dtype=None, + primary=None, sentinel=None, wide_mask_maxbits=None, metadata=None, + cov_pixels=None, bit_packed=False): """ Make an empty map with the same parameters as an existing map. @@ -404,17 +314,9 @@ def make_empty_like( if metadata is None: metadata = sparsemap._metadata - return cls.make_empty( - nside_coverage, - nside_sparse, - dtype, - primary=primary, - sentinel=sentinel, - wide_mask_maxbits=wide_mask_maxbits, - metadata=metadata, - cov_pixels=cov_pixels, - bit_packed=bit_packed, - ) + return cls.make_empty(nside_coverage, nside_sparse, dtype, primary=primary, + sentinel=sentinel, wide_mask_maxbits=wide_mask_maxbits, + metadata=metadata, cov_pixels=cov_pixels, bit_packed=bit_packed) @classmethod def from_data( @@ -463,9 +365,7 @@ def from_data( return sparse_map @staticmethod - def convert_healpix_map( - healpix_map, nside_coverage, nest=True, sentinel=hpg.UNSEEN - ): + def convert_healpix_map(healpix_map, nside_coverage, nest=True, sentinel=hpg.UNSEEN): """ Convert a healpix map to a healsparsemap. @@ -493,7 +393,7 @@ def convert_healpix_map( # Compute the coverage map... # Note that this is coming from a standard healpix map so the sentinel # is always hpg.UNSEEN - (ipnest,) = np.where(healpix_map > hpg.UNSEEN) + ipnest, = np.where(healpix_map > hpg.UNSEEN) nside_sparse = hpg.npixel_to_nside(healpix_map.size) cov_map = HealSparseCoverage.make_empty(nside_coverage, nside_sparse) @@ -503,24 +403,13 @@ def convert_healpix_map( cov_map.initialize_pixels(cov_pix) - sparse_map = np.full( - (cov_pix.size + 1) * cov_map.nfine_per_cov, - sentinel, - dtype=healpix_map.dtype, - ) + sparse_map = np.full((cov_pix.size + 1)*cov_map.nfine_per_cov, + sentinel, dtype=healpix_map.dtype) sparse_map[ipnest + cov_map[ipnest_cov]] = healpix_map[ipnest] return cov_map, sparse_map - def write( - self, - filename, - clobber=False, - nocompress=False, - format="fits", - nside_io=4, - hdf5_group="map", - ): + def write(self, filename, clobber=False, nocompress=False, format='fits', nside_io=4, hdf5_group='map'): """ Write a HealSparseMap to a file. Use the `metadata` property from the map to persist additional information in the fits header. @@ -551,15 +440,8 @@ def write( NotImplementedError if file format is not supported. ValueError if nside_io is out of range. """ - _write_map( - self, - filename, - clobber=clobber, - nocompress=nocompress, - format=format, - nside_io=nside_io, - hdf5_group=hdf5_group, - ) + _write_map(self, filename, clobber=clobber, nocompress=nocompress, format=format, + nside_io=nside_io, hdf5_group=hdf5_group) def write_moc(self, filename, clobber=False): """ @@ -586,14 +468,12 @@ def _reserve_cov_pix(self, new_cov_pix): Integer array of new coverage pixels """ - new_cov_map = self._cov_map.append_pixels( - len(self._sparse_map), new_cov_pix, check=False - ) + new_cov_map = self._cov_map.append_pixels(len(self._sparse_map), new_cov_pix, check=False) self._cov_map = new_cov_map # Use resizing oldsize = len(self._sparse_map) - newsize = oldsize + new_cov_pix.size * self._cov_map.nfine_per_cov + newsize = oldsize + new_cov_pix.size*self._cov_map.nfine_per_cov if self._is_wide_mask: self._sparse_map.resize((newsize, self._wide_mask_width), refcheck=False) @@ -603,15 +483,8 @@ def _reserve_cov_pix(self, new_cov_pix): # Fill with blank values self._sparse_map[oldsize:] = self._sparse_map[0] - def update_values_pos( - self, - ra_or_theta, - dec_or_phi, - values, - lonlat=True, - operation="replace", - check_unique=True, - ): + def update_values_pos(self, ra_or_theta, dec_or_phi, values, + lonlat=True, operation='replace', check_unique=True): """ Update the values in the sparsemap for a list of positions. @@ -658,9 +531,7 @@ def update_values_pos( check_unique=check_unique, ) - def update_values_pix( - self, pixels, values, nest=True, operation="replace", check_unique=True - ): + def update_values_pix(self, pixels, values, nest=True, operation='replace'): """ Update the values in the sparsemap for a list of pixels. The list of pixels must be unique if the operation is 'replace'. @@ -700,7 +571,7 @@ def update_values_pix( # When None is specified, we use the sentinel value. no_append = False if values is None: - if operation != "replace": + if operation != 'replace': raise ValueError("Can only use 'None' with 'replace' operation.") if self._is_wide_mask: @@ -712,24 +583,18 @@ def update_values_pix( values = self._sentinel no_append = True - if operation != "replace": + if operation != 'replace': if self.dtype == np.bool_: - if operation not in ["or", "and"]: - raise NotImplementedError( - "Booleam maps Can only use replace/and/or operations." - ) - elif operation in ["or", "and"]: + if operation not in ['or', 'and']: + raise NotImplementedError("Booleam maps Can only use replace/and/or operations.") + elif operation in ['or', 'and']: if not self.is_integer_map or self._sentinel != 0: - raise ValueError( - "Can only use and/or with integer map with 0 sentinel" - ) - elif operation == "add": + raise ValueError("Can only use and/or with integer map with 0 sentinel") + elif operation == 'add': if self._is_rec_array: raise ValueError("Cannot use 'add' operation with a recarray map.") else: - raise ValueError( - "Only 'replace', 'add', 'or', and 'and' are supported operations" - ) + raise ValueError("Only 'replace', 'add', 'or', and 'and' are supported operations") # If _not_ recarray, we can use a single int/float is_single_value = False @@ -752,9 +617,7 @@ def update_values_pix( _values = np.asarray([values], dtype=self.dtype) elif isinstance(values, numbers.Real): if self.is_integer_map: - raise ValueError( - "Cannot set non-floating point map with a floating point." - ) + raise ValueError("Cannot set non-floating point map with a floating point.") is_single_value = True _values = np.asarray([values], dtype=self.dtype) elif isinstance(values, (bool, np.bool_)): @@ -770,11 +633,9 @@ def update_values_pix( if hasattr(pixels, "__len__") and len(pixels) == 0: if len(_values) > 1: - warnings.warn( - "Shape mismatch: using a non-zero-length array of values " - "to set a zero-length list of pixels.", - UserWarning, - ) + warnings.warn("Shape mismatch: using a non-zero-length array of values " + "to set a zero-length list of pixels.", + UserWarning) # Nothing to do return @@ -796,9 +657,7 @@ def update_values_pix( if pixels.ndim == 2 and pixels.shape[1] == 2: # These are pixel ranges. if not is_single_value: - raise ValueError( - "Can only use a single value with pixel ranges (N, 2) input." - ) + raise ValueError("Can only use a single value with pixel ranges (N, 2) input.") if not nest: raise ValueError("Can only use pixel ranges with nest ordering.") @@ -806,9 +665,7 @@ def update_values_pix( # if the number of pixels is above some threshold. pixels_to_set = np.sum(pixels[:, 1] - pixels[:, 0]) if pixels_to_set > PIXEL_RANGE_THRESHOLD: - return self._update_values_pixel_ranges( - pixels, _values[0], operation, no_append - ) + return self._update_values_pixel_ranges(pixels, _values[0], operation, no_append) else: _pix = hpg.pixel_ranges_to_pixels(pixels) elif not nest: @@ -818,16 +675,12 @@ def update_values_pix( # Check array lengths if not is_single_value and len(_values) != pixels.size: - raise ValueError( - "Length of values must be same length as pixels (or length 1)" - ) + raise ValueError("Length of values must be same length as pixels (or length 1)") if self._is_view: # Check that we are not setting new pixels if np.any(self.get_values_pix(_pix) == self._sentinel): - raise RuntimeError( - "This API cannot be used to set new pixels in the map." - ) + raise RuntimeError("This API cannot be used to set new pixels in the map.") # Compute the coverage pixels ipnest_cov = self._cov_map.cov_pixels(_pix) @@ -861,13 +714,9 @@ def _do_operation_on_sparse_map(operation, sparse_map, indices, values): # Replace values for those pixels in the coverage map _indices = _pix[in_cov] + self._cov_map[ipnest_cov[in_cov]] if is_single_value: - _do_operation_on_sparse_map( - operation, self._sparse_map, _indices, _values[0] - ) + _do_operation_on_sparse_map(operation, self._sparse_map, _indices, _values[0]) else: - _do_operation_on_sparse_map( - operation, self._sparse_map, _indices, _values[in_cov] - ) + _do_operation_on_sparse_map(operation, self._sparse_map, _indices, _values[in_cov]) # Update the coverage map for the rest of the pixels (if necessary) if out_cov.sum() > 0 and not no_append: @@ -876,7 +725,7 @@ def _do_operation_on_sparse_map(operation, sparse_map, indices, values): # Faster trick for getting unique values new_cov_temp = np.zeros(cov_mask.size, dtype=np.int8) new_cov_temp[ipnest_cov[out_cov]] = 1 - (new_cov_pix,) = np.where(new_cov_temp > 0) + new_cov_pix, = np.where(new_cov_temp > 0) # Reserve the memory here oldsize = len(self._sparse_map) @@ -884,13 +733,9 @@ def _do_operation_on_sparse_map(operation, sparse_map, indices, values): _indices = _pix[out_cov] + self._cov_map[ipnest_cov[out_cov]] - oldsize if is_single_value: - _do_operation_on_sparse_map( - operation, self._sparse_map[oldsize:], _indices, _values[0] - ) + _do_operation_on_sparse_map(operation, self._sparse_map[oldsize:], _indices, _values[0]) else: - _do_operation_on_sparse_map( - operation, self._sparse_map[oldsize:], _indices, _values[out_cov] - ) + _do_operation_on_sparse_map(operation, self._sparse_map[oldsize:], _indices, _values[out_cov]) def _update_values_pixel_ranges(self, pixel_ranges, value, operation, no_append): """ @@ -931,31 +776,28 @@ def _update_values_pixel_ranges(self, pixel_ranges, value, operation, no_append) delta_pix = pixel_ranges[:, 1] - pixel_ranges[:, 0] delta_covpix = cov_pix_ranges[:, 1] - cov_pix_ranges[:, 0] - covpix_start_values = ( - self._cov_map[cov_pix_ranges.ravel()] + - self._cov_map.nfine_per_cov * cov_pix_ranges.ravel() - ).reshape(cov_pix_ranges.shape) + covpix_start_values = (self._cov_map[cov_pix_ranges.ravel()] + + self._cov_map.nfine_per_cov*cov_pix_ranges.ravel() + ).reshape(cov_pix_ranges.shape) - covpix_offset_values = self._cov_map[ - self._cov_map.cov_pixels_from_index(covpix_start_values.ravel()) - ].reshape(cov_pix_ranges.shape) + covpix_offset_values = self._cov_map[self._cov_map.cov_pixels_from_index( + covpix_start_values.ravel() + )].reshape(cov_pix_ranges.shape) - def _do_operation_on_sparse_map_range( - operation, sparse_map, start, stop, value - ): + def _do_operation_on_sparse_map_range(operation, sparse_map, start, stop, value): # Note that start: stop will not have overlapping pixels, so we do # not need to use ufunc.at() to perform operations. if operation == "replace": - sparse_map[start:stop] = value + sparse_map[start: stop] = value elif operation == "add": # Put in a check to reset uncovered pixels to 0 if self._sentinel != 0: - sparse_map[start:stop][sparse_map[start:stop] == self._sentinel] = 0 - sparse_map[start:stop] += value + sparse_map[start: stop][sparse_map[start: stop] == self._sentinel] = 0 + sparse_map[start: stop] += value elif operation == "or": - sparse_map[start:stop] |= value + sparse_map[start: stop] |= value elif operation == "and": - sparse_map[start:stop] &= value + sparse_map[start: stop] &= value # Loop over ranges. for i in range(pixel_ranges.shape[0]): @@ -967,42 +809,31 @@ def _do_operation_on_sparse_map_range( else: # The first coverage pixel will be partly covered. start = pixel_ranges[i, 0] + covpix_offset_values[i, 0] - stop = self._cov_map[ - cov_pix_ranges[i, 0] - ] + self._cov_map.nfine_per_cov * (cov_pix_ranges[i, 0] + 1) - _do_operation_on_sparse_map_range( - operation, self._sparse_map, start, stop, value + stop = ( + self._cov_map[cov_pix_ranges[i, 0]] + + self._cov_map.nfine_per_cov*(cov_pix_ranges[i, 0] + 1) ) + _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) # The middle coverage pixels will be fully covered. - for cov_pix_full in range( - cov_pix_ranges[i, 0] + 1, cov_pix_ranges[i, 1] - ): + for cov_pix_full in range(cov_pix_ranges[i, 0] + 1, cov_pix_ranges[i, 1]): if no_append and not cov_mask[cov_pix_full]: # Nothing to set here. continue - start = ( - self._cov_map[cov_pix_full] + - self._cov_map.nfine_per_cov * cov_pix_full - ) + start = (self._cov_map[cov_pix_full] + self._cov_map.nfine_per_cov*cov_pix_full) stop = start + self._cov_map.nfine_per_cov - _do_operation_on_sparse_map_range( - operation, self._sparse_map, start, stop, value - ) + _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) if no_append and not cov_mask[cov_pix_ranges[i, 1]]: # Nothing to set here. pass else: # The final coverage pixel will be partly covered. - start = ( - self._cov_map[cov_pix_ranges[i, 1]] + - self._cov_map.nfine_per_cov * (cov_pix_ranges[i, 1]) - ) + start = (self._cov_map[cov_pix_ranges[i, 1]] + + self._cov_map.nfine_per_cov*(cov_pix_ranges[i, 1]) + ) stop = pixel_ranges[i, 1] + covpix_offset_values[i, 1] - _do_operation_on_sparse_map_range( - operation, self._sparse_map, start, stop, value - ) + _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) else: if no_append and not cov_mask[cov_pix_ranges[i, 0]]: # Nothing to set here. @@ -1012,9 +843,7 @@ def _do_operation_on_sparse_map_range( start = pixel_ranges[i, 0] + covpix_offset_values[i, 0] stop = start + delta_pix[i] - _do_operation_on_sparse_map_range( - operation, self._sparse_map, start, stop, value - ) + _do_operation_on_sparse_map_range(operation, self._sparse_map, start, stop, value) def set_bits_pix(self, pixels, bits, nest=True): """ @@ -1031,14 +860,12 @@ def set_bits_pix(self, pixels, bits, nest=True): raise NotImplementedError("Can only use set_bits_pix on wide_mask map") if np.max(bits) >= self._wide_mask_maxbits: - raise ValueError( - "Bit position %d too large (>= %d)" - % (np.max(bits), self._wide_mask_maxbits) - ) + raise ValueError("Bit position %d too large (>= %d)" % (np.max(bits), + self._wide_mask_maxbits)) value = _bitvals_to_packed_array(bits, self._wide_mask_maxbits) - self.update_values_pix(pixels, value, nest=nest, operation="or") + self.update_values_pix(pixels, value, nest=nest, operation='or') def clear_bits_pix(self, pixels, bits, nest=True): """ @@ -1055,15 +882,13 @@ def clear_bits_pix(self, pixels, bits, nest=True): raise NotImplementedError("Can only use set_bits_pix on wide_mask map") if np.max(bits) >= self._wide_mask_maxbits: - raise ValueError( - "Bit position %d too large (>= %d)" - % (np.max(bits), self._wide_mask_maxbits) - ) + raise ValueError("Bit position %d too large (>= %d)" % (np.max(bits), + self._wide_mask_maxbits)) value = _bitvals_to_packed_array(bits, self._wide_mask_maxbits) # A bit reset is performed with &= ~(bit1 | bit2) - self.update_values_pix(pixels, ~value, nest=nest, operation="and") + self.update_values_pix(pixels, ~value, nest=nest, operation='and') def get_values_pos(self, ra_or_theta, dec_or_phi, lonlat=True, valid_mask=False): """ @@ -1088,12 +913,11 @@ def get_values_pos(self, ra_or_theta, dec_or_phi, lonlat=True, valid_mask=False) values : `np.ndarray` Array of values/validity from the map. """ - return self.get_values_pix( - hpg.angle_to_pixel( - self._nside_sparse, ra_or_theta, dec_or_phi, lonlat=lonlat - ), - valid_mask=valid_mask, - ) + return self.get_values_pix(hpg.angle_to_pixel(self._nside_sparse, + ra_or_theta, + dec_or_phi, + lonlat=lonlat), + valid_mask=valid_mask) def get_values_pix(self, pixels, nest=True, valid_mask=False, nside=None): """ @@ -1146,11 +970,11 @@ def get_values_pix(self, pixels, nest=True, valid_mask=False, nside=None): if valid_mask: if self._is_rec_array: - return values[self._primary] != self._sentinel + return (values[self._primary] != self._sentinel) elif self._is_wide_mask: return (values > 0).sum(axis=1, dtype=np.bool_) else: - return values != self._sentinel + return (values != self._sentinel) else: # Just return the values return values @@ -1179,12 +1003,11 @@ def check_bits_pos(self, ra_or_theta, dec_or_phi, bits, lonlat=True): Array of `np.bool_` flags on whether any of the input bits were set """ - return self.check_bits_pix( - hpg.angle_to_pixel( - self._nside_sparse, ra_or_theta, dec_or_phi, lonlat=lonlat - ), - bits, - ) + return self.check_bits_pix(hpg.angle_to_pixel(self._nside_sparse, + ra_or_theta, + dec_or_phi, + lonlat=lonlat), + bits) def check_bits_pix(self, pixels, bits, nest=True): """ @@ -1240,27 +1063,27 @@ def coverage_map(self): cov_mask = self.coverage_mask npop_pix = np.count_nonzero(cov_mask) if self._is_wide_mask: - shape_new = ( - npop_pix + 1, - self._cov_map.nfine_per_cov, - self._wide_mask_width, - ) + shape_new = (npop_pix + 1, + self._cov_map.nfine_per_cov, + self._wide_mask_width) sp_map_t = self._sparse_map.reshape(shape_new) # This trickery first checks all the bits, and then sums into the # coverage pixel counts = np.sum(np.any(sp_map_t != self._sentinel, axis=2), axis=1) elif self._is_bit_packed: - shape_new = (npop_pix + 1, self._cov_map.nfine_per_cov) + shape_new = (npop_pix + 1, + self._cov_map.nfine_per_cov) counts = self._sparse_map.sum(shape=shape_new, axis=1).astype(np.float64) else: - shape_new = (npop_pix + 1, self._cov_map.nfine_per_cov) + shape_new = (npop_pix + 1, + self._cov_map.nfine_per_cov) if self._is_rec_array: sp_map_t = self._sparse_map[self._primary].reshape(shape_new) else: sp_map_t = self._sparse_map.reshape(shape_new) counts = np.sum((sp_map_t != self._sentinel), axis=1).astype(np.float64) - cov_map[cov_mask] = counts[1:] / self._cov_map.nfine_per_cov + cov_map[cov_mask] = counts[1:]/self._cov_map.nfine_per_cov return cov_map @property @@ -1297,15 +1120,11 @@ def fracdet_map(self, nside): Fractional coverage map. """ if nside > self.nside_sparse: - raise ValueError( - "Cannot return fracdet_map at higher resolution than " - "the sparse map (nside=%d)." % (self.nside_sparse) - ) + raise ValueError("Cannot return fracdet_map at higher resolution than " + "the sparse map (nside=%d)." % (self.nside_sparse)) if nside < self.nside_coverage: - raise ValueError( - "Cannot return fractdet_map at lower resolution than " - "the coverage map (nside=%d)." % (self.nside_coverage) - ) + raise ValueError("Cannot return fractdet_map at lower resolution than " + "the coverage map (nside=%d)." % (self.nside_coverage)) # This code is essentially a unification of coverage_map() and degrade() # to get the fracdet_coverage in a single step @@ -1314,23 +1133,20 @@ def fracdet_map(self, nside): bit_shift = _compute_bitshift(nside, self.nside_sparse) nfine_per_frac = 2**bit_shift - nfrac_per_cov = self._cov_map.nfine_per_cov // nfine_per_frac + nfrac_per_cov = self._cov_map.nfine_per_cov//nfine_per_frac if self._is_wide_mask: - shape_new = ( - (npop_pix + 1) * nfrac_per_cov, - nfine_per_frac, - self._wide_mask_width, - ) + shape_new = ((npop_pix + 1)*nfrac_per_cov, + nfine_per_frac, + self._wide_mask_width) sp_map_t = self._sparse_map.reshape(shape_new) - fracdet = np.sum(np.any(sp_map_t != self._sentinel, axis=2), axis=1).astype( - np.float64 - ) + fracdet = np.sum(np.any(sp_map_t != self._sentinel, axis=2), axis=1).astype(np.float64) elif self._is_bit_packed: - shape_new = ((npop_pix + 1) * nfrac_per_cov, nfine_per_frac) + shape_new = ((npop_pix + 1)*nfrac_per_cov, nfine_per_frac) fracdet = self._sparse_map.sum(shape=shape_new, axis=1).astype(np.float64) else: - shape_new = ((npop_pix + 1) * nfrac_per_cov, nfine_per_frac) + shape_new = ((npop_pix + 1)*nfrac_per_cov, + nfine_per_frac) if self._is_rec_array: sp_map_t = self._sparse_map[self._primary].reshape(shape_new) else: @@ -1339,18 +1155,14 @@ def fracdet_map(self, nside): fracdet /= nfine_per_frac - fracdet_cov_map = HealSparseCoverage.make_from_pixels( - self.nside_coverage, nside, np.where(cov_mask)[0] - ) + fracdet_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, + nside, + np.where(cov_mask)[0]) # The sentinel for a fracdet_map is 0.0, no coverage. - return HealSparseMap( - cov_map=fracdet_cov_map, - sparse_map=fracdet, - nside_sparse=nside, - primary=self._primary, - sentinel=0.0, - ) + return HealSparseMap(cov_map=fracdet_cov_map, sparse_map=fracdet, + nside_sparse=nside, primary=self._primary, + sentinel=0.0) @property def nside_coverage(self): @@ -1517,7 +1329,7 @@ def metadata(self, metadata): self._metadata = metadata - def generate_healpix_map(self, nside=None, reduction="mean", key=None, nest=True): + def generate_healpix_map(self, nside=None, reduction='mean', key=None, nest=True): """ Generate the associated healpix map @@ -1551,9 +1363,7 @@ def generate_healpix_map(self, nside=None, reduction="mean", key=None, nest=True if self._is_rec_array: if key is None: - raise ValueError( - "key should be specified for HealSparseMaps including `recarray`" - ) + raise ValueError('key should be specified for HealSparseMaps including `recarray`') else: # This is memory inefficient in that we are copying the memory # to ensure that we get a unique healpix map. To not get a copy, @@ -1569,9 +1379,7 @@ def generate_healpix_map(self, nside=None, reduction="mean", key=None, nest=True # degrade to new resolution single_map = single_map.degrade(nside, reduction=reduction) elif nside > self._nside_sparse: - raise ValueError( - "Cannot generate HEALPix map with higher resolution than the original." - ) + raise ValueError("Cannot generate HEALPix map with higher resolution than the original.") # Check to see if we have an integer or boolean map. if issubclass(single_map._sparse_map.dtype.type, (np.integer, np.bool_, bool)): @@ -1599,26 +1407,17 @@ def valid_pixels(self): valid_pixels : `np.ndarray` """ if self._is_rec_array: - (valid_pixel_inds,) = np.where( - self._sparse_map[self._primary] != self._sentinel - ) + valid_pixel_inds, = np.where(self._sparse_map[self._primary] != self._sentinel) elif self._is_wide_mask: - (valid_pixel_inds,) = np.where( - np.any(self._sparse_map != self._sentinel, axis=1) - ) + valid_pixel_inds, = np.where(np.any(self._sparse_map != self._sentinel, axis=1)) elif self._is_bit_packed: # This is dangerous because it expands into a full array first; this # can blow up memory. - (valid_pixel_inds,) = np.where( - np.asarray(self._sparse_map) != self._sentinel - ) + valid_pixel_inds, = np.where(np.asarray(self._sparse_map) != self._sentinel) else: - (valid_pixel_inds,) = np.where(self._sparse_map != self._sentinel) + valid_pixel_inds, = np.where(self._sparse_map != self._sentinel) - return ( - valid_pixel_inds - - self._cov_map[self._cov_map.cov_pixels_from_index(valid_pixel_inds)] - ) + return valid_pixel_inds - self._cov_map[self._cov_map.cov_pixels_from_index(valid_pixel_inds)] def valid_pixels_pos(self, lonlat=True, return_pixels=False): """ @@ -1643,14 +1442,10 @@ def valid_pixels_pos(self, lonlat=True, return_pixels=False): """ if return_pixels: valid_pixels = self.valid_pixels - lon, lat = hpg.pixel_to_angle( - self.nside_sparse, valid_pixels, lonlat=lonlat - ) + lon, lat = hpg.pixel_to_angle(self.nside_sparse, valid_pixels, lonlat=lonlat) return (valid_pixels, lon, lat) else: - return hpg.pixel_to_angle( - self.nside_sparse, self.valid_pixels, lonlat=lonlat - ) + return hpg.pixel_to_angle(self.nside_sparse, self.valid_pixels, lonlat=lonlat) @property def n_valid(self): @@ -1694,7 +1489,7 @@ def iter_valid_pixels_by_covpix(self): >>> for valid_pixels in m.iter_valid_pixels_by_covpix(): ... print(valid_pixels) """ - (cov_pixels,) = np.where(self._cov_map.coverage_mask) + cov_pixels, = np.where(self._cov_map.coverage_mask) for cov_pix in cov_pixels: yield self.valid_pixels_single_covpix(cov_pix) @@ -1720,30 +1515,23 @@ def valid_pixels_single_covpix(self, cov_pix): return np.asarray([], dtype=np.int64) # This is the start of the coverage pixel slice. - start = self._cov_map[cov_pix] + self._cov_map.nfine_per_cov * cov_pix + start = (self._cov_map[cov_pix] + + self._cov_map.nfine_per_cov*cov_pix) s = slice(start, start + self._cov_map.nfine_per_cov) if self._is_rec_array: - (valid_pixel_inds,) = np.where( - self._sparse_map[self._primary][s] != self._sentinel - ) + valid_pixel_inds, = np.where(self._sparse_map[self._primary][s] != self._sentinel) elif self._is_wide_mask: - (valid_pixel_inds,) = np.where( - np.any(self._sparse_map[s, :] != self._sentinel, axis=1) - ) + valid_pixel_inds, = np.where(np.any(self._sparse_map[s, :] != self._sentinel, axis=1)) elif self._is_bit_packed: - (valid_pixel_inds,) = np.where( - np.asarray(self._sparse_map[s]) != self._sentinel - ) + valid_pixel_inds, = np.where(np.asarray(self._sparse_map[s]) != self._sentinel) else: - (valid_pixel_inds,) = np.where(self._sparse_map[s] != self._sentinel) + valid_pixel_inds, = np.where(self._sparse_map[s] != self._sentinel) # We need to get the correct offsets for our valid pixel subset. - return ( - valid_pixel_inds - - self._cov_map[self._cov_map.cov_pixels_from_index(start)] + - start - ) + return (valid_pixel_inds - + self._cov_map[self._cov_map.cov_pixels_from_index(start)] + + start) def get_valid_area(self, degrees=True): """ @@ -1758,11 +1546,9 @@ def get_valid_area(self, degrees=True): ------- valid_area : `float` """ - return self.n_valid * hpg.nside_to_pixel_area( - self._nside_sparse, degrees=degrees - ) + return self.n_valid*hpg.nside_to_pixel_area(self._nside_sparse, degrees=degrees) - def _degrade(self, nside_out, reduction="mean", weights=None): + def _degrade(self, nside_out, reduction='mean', weights=None): """ Auxiliary method to reduce the resolution, i.e., increase the pixel size of a given sparse map (which is called by `degrade`). @@ -1783,9 +1569,7 @@ def _degrade(self, nside_out, reduction="mean", weights=None): New map, at the desired resolution. """ if self._nside_sparse < nside_out: - raise ValueError( - "nside_out should be smaller than nside for the sparse_map." - ) + raise ValueError('nside_out should be smaller than nside for the sparse_map.') # Count the number of filled pixels in the coverage mask npop_pix = np.count_nonzero(self.coverage_mask) # We need the new bit_shifts and we have to build a new CovIndexMap @@ -1795,56 +1579,41 @@ def _degrade(self, nside_out, reduction="mean", weights=None): # Check weights and add guards weight_values = None if weights is not None: - if reduction != "wmean": - warnings.warn( - "Weights only used with wmean reduction. Ignoring weights.", - UserWarning, - ) + if reduction != 'wmean': + warnings.warn('Weights only used with wmean reduction. Ignoring weights.', + UserWarning) else: # Check format/size of weight-map here. if not isinstance(weights, HealSparseMap): raise ValueError("weights must be a HealSparseMap.") - if ( - weights.is_rec_array or - weights.is_wide_mask_map or - weights.is_integer_map - ): + if weights.is_rec_array or weights.is_wide_mask_map or weights.is_integer_map: raise ValueError("weights must be a floating-point map.") - bad_map = ( - (weights.nside_sparse != self.nside_sparse) or - (weights.nside_coverage != self.nside_coverage) or - (not np.array_equal(weights.valid_pixels, self.valid_pixels)) - ) + bad_map = ((weights.nside_sparse != self.nside_sparse) or + (weights.nside_coverage != self.nside_coverage) or + (not np.array_equal(weights.valid_pixels, self.valid_pixels))) if bad_map: - raise ValueError("weights dimensions must be the same as this map.") + raise ValueError('weights dimensions must be the same as this map.') weight_values = weights._sparse_map # Set to zero weight those pixels that are not observed # This is valid for all types of maps because they share the same valid_pixels. weight_values[weight_values == weights._sentinel] = 0.0 - weight_values = weight_values.reshape( - (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) - ) - elif reduction == "wmean": - raise ValueError("Must specify weights when using wmean reduction.") + weight_values = weight_values.reshape((npop_pix + 1, + (nside_out//self.nside_coverage)**2, -1)) + elif reduction == 'wmean': + raise ValueError('Must specify weights when using wmean reduction.') # At this point, the weight map has been checked and will only be used if # the reduction is set to wmean. # Work with wide masks if self._is_wide_mask: - if reduction not in ["and", "or"]: - raise NotImplementedError( - "Cannot degrade a wide_mask map with this \ - reduction operation, try and/or." - ) + if reduction not in ['and', 'or']: + raise NotImplementedError('Cannot degrade a wide_mask map with this \ + reduction operation, try and/or.') else: nbits = self._sparse_map.shape[1] - aux = self._sparse_map.reshape( - (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1, nbits) - ) - sparse_map_out = reduce_array(aux, reduction=reduction, axis=2).reshape( - (-1, nbits) - ) + aux = self._sparse_map.reshape((npop_pix+1, (nside_out//self.nside_coverage)**2, -1, nbits)) + sparse_map_out = reduce_array(aux, reduction=reduction, axis=2).reshape((-1, nbits)) sentinel_out = self._sentinel # Work with RecArray (we have to change the resolution to all maps...) @@ -1858,13 +1627,11 @@ def _degrade(self, nside_out, reduction="mean", weights=None): else: dtype.append((key, value[0])) # Allocate new map - sparse_map_out = np.zeros((npop_pix + 1) * nfine_per_cov, dtype=dtype) + sparse_map_out = np.zeros((npop_pix + 1)*nfine_per_cov, dtype=dtype) for key, value in sparse_map_out.dtype.fields.items(): aux = self._sparse_map[key].astype(np.float64) aux[self._sparse_map[self._primary] == self._sentinel] = np.nan - aux = aux.reshape( - (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) - ) + aux = aux.reshape((npop_pix + 1, (nside_out//self.nside_coverage)**2, -1)) # Perform the reduction operation (check utils.reduce_array) aux = reduce_array(aux, reduction=reduction, weights=weight_values) # Transform back to sentinel value @@ -1872,12 +1639,8 @@ def _degrade(self, nside_out, reduction="mean", weights=None): sparse_map_out[key] = aux # Work with int array and ndarray - elif (issubclass(self._sparse_map.dtype.type, np.integer)) and ( - reduction in ["and", "or"] - ): - aux = self._sparse_map.reshape( - (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) - ) + elif (issubclass(self._sparse_map.dtype.type, np.integer)) and (reduction in ['and', 'or']): + aux = self._sparse_map.reshape((npop_pix+1, (nside_out//self.nside_coverage)**2, -1)) sparse_map_out = reduce_array(aux, reduction=reduction) sentinel_out = self._sentinel else: @@ -1888,9 +1651,7 @@ def _degrade(self, nside_out, reduction="mean", weights=None): sentinel_out = hpg.UNSEEN aux = self._sparse_map.astype(aux_dtype) aux[self._sparse_map == self._sentinel] = np.nan - aux = aux.reshape( - (npop_pix + 1, (nside_out // self.nside_coverage) ** 2, -1) - ) + aux = aux.reshape((npop_pix + 1, (nside_out//self.nside_coverage)**2, -1)) aux = reduce_array(aux, reduction=reduction, weights=weight_values) # NaN are converted to UNSEEN aux[np.isnan(aux)] = sentinel_out @@ -1898,18 +1659,13 @@ def _degrade(self, nside_out, reduction="mean", weights=None): # The coverage index map is now offset, we have to build a new one # Note that we need to keep the same order of the coverage map - new_cov_map = HealSparseCoverage.make_from_pixels( - self.nside_coverage, nside_out, self._cov_map._block_to_cov_index - ) - return HealSparseMap( - cov_map=new_cov_map, - sparse_map=sparse_map_out, - nside_sparse=nside_out, - primary=self._primary, - sentinel=sentinel_out, - ) + new_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, + nside_out, + self._cov_map._block_to_cov_index) + return HealSparseMap(cov_map=new_cov_map, sparse_map=sparse_map_out, + nside_sparse=nside_out, primary=self._primary, sentinel=sentinel_out) - def degrade(self, nside_out, reduction="mean", weights=None): + def degrade(self, nside_out, reduction='mean', weights=None): """ Decrease the resolution of the map, i.e., increase the pixel size. @@ -1936,56 +1692,41 @@ def degrade(self, nside_out, reduction="mean", weights=None): if self._is_bit_packed: if weights is not None: - raise NotImplementedError( - "Map degrading with weights is not supported for bit_packed maps." - ) + raise NotImplementedError("Map degrading with weights is not supported for bit_packed maps.") elif nside_out < self.nside_coverage: raise NotImplementedError( "Map degrading below nside_coverage is not supported for bit_packed maps.", ) # Do coverage-pixel by coverage-pixel: - sparse_map_out = HealSparseMap.make_empty( - self.nside_coverage, nside_out, dtype=np.float64 - ) + sparse_map_out = HealSparseMap.make_empty(self.nside_coverage, nside_out, dtype=np.float64) for covpix_map in self.get_covpix_maps(): - covpix_degrade = covpix_map.astype(np.bool_)._degrade( - nside_out, reduction=reduction - ) - sparse_map_out[covpix_degrade.valid_pixels] = covpix_degrade[ - covpix_degrade.valid_pixels - ] + covpix_degrade = covpix_map.astype(np.bool_)._degrade(nside_out, reduction=reduction) + sparse_map_out[covpix_degrade.valid_pixels] = covpix_degrade[covpix_degrade.valid_pixels] return sparse_map_out if nside_out < self.nside_coverage: # The way we do the reduction requires nside_out to be >= nside_coverage # we allocate a new map with the required nside_out # CAUTION: This may require a lot of memory!! - warnings.warn( - "`nside_out` < `nside_coverage`. \ + warnings.warn("`nside_out` < `nside_coverage`. \ Allocating new map with nside_coverage=nside_out", - ResourceWarning, - ) - sparse_map_out = HealSparseMap.make_empty_like( - self, nside_coverage=nside_out - ) + ResourceWarning) + sparse_map_out = HealSparseMap.make_empty_like(self, + nside_coverage=nside_out) if weights is not None: wgt_valid = weights.valid_pixels - _weights = HealSparseMap.make_empty_like( - weights, nside_coverage=nside_out - ) + _weights = HealSparseMap.make_empty_like(weights, nside_coverage=nside_out) _weights[wgt_valid] = weights[wgt_valid] weights = _weights valid_pixels = self.valid_pixels sparse_map_out[valid_pixels] = self[valid_pixels] - sparse_map_out = sparse_map_out._degrade( - nside_out, reduction=reduction, weights=weights - ) + sparse_map_out = sparse_map_out._degrade(nside_out, reduction=reduction, weights=weights) else: # Regular degrade - sparse_map_out = self._degrade( - nside_out, reduction=reduction, weights=weights - ) + sparse_map_out = self._degrade(nside_out, + reduction=reduction, + weights=weights) return sparse_map_out @@ -2014,22 +1755,18 @@ def upgrade(self, nside_out): raise NotImplementedError("Upgrading bit_packed maps is not yet supported.") # Make an order preserving coverage map. - new_cov_map = HealSparseCoverage.make_from_pixels( - self.nside_coverage, nside_out, self._cov_map._block_to_cov_index - ) + new_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, + nside_out, + self._cov_map._block_to_cov_index) # And a new sparse map bit_shift = _compute_bitshift(self._nside_sparse, nside_out) nout_per_self = 2**bit_shift # Nest maps at higher resolution are just repeats of the same values new_sparse_map = np.repeat(self._sparse_map, nout_per_self) - return HealSparseMap( - cov_map=new_cov_map, - sparse_map=new_sparse_map, - nside_sparse=nside_out, - primary=self._primary, - sentinel=self._sentinel, - ) + return HealSparseMap(cov_map=new_cov_map, sparse_map=new_sparse_map, + nside_sparse=nside_out, primary=self._primary, + sentinel=self._sentinel) def apply_mask(self, mask_map, mask_bits=None, mask_bit_arr=None, in_place=True): """ @@ -2068,48 +1805,34 @@ def apply_mask(self, mask_map, mask_bits=None, mask_bit_arr=None, in_place=True) if mask_bits is None: if mask_map.is_wide_mask_map: if mask_bit_arr is None: - (bad_pixels,) = np.where( - mask_map.get_values_pix(valid_pixels).sum(axis=1) > 0 - ) + bad_pixels, = np.where(mask_map.get_values_pix(valid_pixels).sum(axis=1) > 0) else: mask_values = mask_map.get_values_pix(valid_pixels) - bit_value = _bitvals_to_packed_array( - mask_bit_arr, mask_map._wide_mask_maxbits - ) - (bad_pixels,) = np.where( - np.any((mask_values & bit_value) > 0, axis=1) - ) + bit_value = _bitvals_to_packed_array(mask_bit_arr, mask_map._wide_mask_maxbits) + bad_pixels, = np.where(np.any((mask_values & bit_value) > 0, axis=1)) else: - (bad_pixels,) = np.where(mask_map.get_values_pix(valid_pixels) > 0) + bad_pixels, = np.where(mask_map.get_values_pix(valid_pixels) > 0) else: - (bad_pixels,) = np.where( - (mask_map.get_values_pix(valid_pixels) & mask_bits) > 0 - ) + bad_pixels, = np.where((mask_map.get_values_pix(valid_pixels) & mask_bits) > 0) if in_place: new_map = self else: - new_map = HealSparseMap( - cov_map=self._cov_map.copy(), - sparse_map=self._sparse_map.copy(), - nside_sparse=self._nside_sparse, - primary=self._primary, - sentinel=self._sentinel, - ) + new_map = HealSparseMap(cov_map=self._cov_map.copy(), + sparse_map=self._sparse_map.copy(), + nside_sparse=self._nside_sparse, + primary=self._primary, + sentinel=self._sentinel) new_value = new_map._sparse_map[0] ipnest_cov = self._cov_map.cov_pixels(valid_pixels[bad_pixels]) - new_map._sparse_map[valid_pixels[bad_pixels] + new_map._cov_map[ipnest_cov]] = ( - new_value - ) + new_map._sparse_map[valid_pixels[bad_pixels] + new_map._cov_map[ipnest_cov]] = new_value return new_map - def interpolate_pos( - self, ra_or_theta, dec_or_phi, lonlat=True, allow_partial=False - ): + def interpolate_pos(self, ra_or_theta, dec_or_phi, lonlat=True, allow_partial=False): """ Return the bilinear interpolation of the map using 4 nearest neighbors. @@ -2153,7 +1876,7 @@ def interpolate_pos( lonlat=lonlat, ) aux = self.get_values_pix(interp_pix) - out_of_bounds = aux == self._sentinel + out_of_bounds = (aux == self._sentinel) aux = aux.astype(np.float64) aux[out_of_bounds] = np.nan @@ -2166,7 +1889,7 @@ def interpolate_pos( interp_wgt[out_of_bounds] = np.nan wgt_sum = np.nansum(interp_wgt, axis=1) values = np.nansum(aux * interp_wgt, axis=1) - all_bad = wgt_sum == 0.0 + all_bad = (wgt_sum == 0.0) values[~all_bad] /= wgt_sum[~all_bad] # Any pixel that has all bad neighbors will be UNSEEN. values[all_bad] = hpg.UNSEEN @@ -2179,9 +1902,7 @@ def __getitem__(self, key): """ if isinstance(key, str): if not self._is_rec_array: - raise IndexError( - "HealSparseMap is not a recarray map, cannot use string index." - ) + raise IndexError("HealSparseMap is not a recarray map, cannot use string index.") return self.get_single(key, sentinel=None) elif isinstance(key, numbers.Integral): # Get a single pixel @@ -2190,11 +1911,7 @@ def __getitem__(self, key): elif isinstance(key, slice): # Get a slice of pixels start = key.start if key.start is not None else 0 - stop = ( - key.stop - if key.stop is not None - else hpg.nside_to_npixel(self._nside_sparse) - ) + stop = key.stop if key.stop is not None else hpg.nside_to_npixel(self._nside_sparse) step = key.step if key.step is not None else 1 return self.get_values_pix(np.arange(start, stop, step)) elif isinstance(key, np.ndarray): @@ -2208,15 +1925,11 @@ def __getitem__(self, key): arr = np.atleast_1d(key) if len(arr) > 0: if not is_integer_value(arr[0]): - raise IndexError( - "List array indices must be integers for __getitem__" - ) + raise IndexError("List array indices must be integers for __getitem__") return self.get_values_pix(arr) else: - raise IndexError( - "Illegal index type (%s) for __getitem__ in HealSparseMap." - % (key.__class__) - ) + raise IndexError("Illegal index type (%s) for __getitem__ in HealSparseMap." % + (key.__class__)) def __setitem__(self, key, value): """ @@ -2228,13 +1941,10 @@ def __setitem__(self, key, value): elif isinstance(key, slice): # Set a slice of pixels start = key.start if key.start is not None else 0 - stop = ( - key.stop - if key.stop is not None - else hpg.nside_to_npixel(self._nside_sparse) - ) + stop = key.stop if key.stop is not None else hpg.nside_to_npixel(self._nside_sparse) step = key.step if key.step is not None else 1 - return self.update_values_pix(np.arange(start, stop, step), value) + return self.update_values_pix(np.arange(start, stop, step), + value) elif isinstance(key, np.ndarray): test_value = np.zeros(1, key.dtype)[0] if not is_integer_value(test_value): @@ -2243,15 +1953,11 @@ def __setitem__(self, key, value): elif isinstance(key, list): arr = np.atleast_1d(key) if len(arr) > 0 and not is_integer_value(arr[0]): - raise IndexError( - "List/Tuple array indices must be integers for __setitem__" - ) + raise IndexError("List/Tuple array indices must be integers for __setitem__") return self.update_values_pix(arr, value) else: - raise IndexError( - "Illegal index type (%s) for __setitem__ in HealSparseMap." - % (key.__class__) - ) + raise IndexError("Illegal index type (%s) for __setitem__ in HealSparseMap." % + (key.__class__)) def get_single(self, key, sentinel=None, copy=False): """ @@ -2285,25 +1991,18 @@ def get_single(self, key, sentinel=None, copy=False): # on how they were constructed (though using make_empty should be safe). # However, these linked maps cannot be used to add new pixels which # is why there is the _is_view flag. - return HealSparseMap( - cov_map=self._cov_map, - sparse_map=self._sparse_map[key], - nside_sparse=self._nside_sparse, - sentinel=_sentinel, - _is_view=True, - ) + return HealSparseMap(cov_map=self._cov_map, + sparse_map=self._sparse_map[key], + nside_sparse=self._nside_sparse, sentinel=_sentinel, + _is_view=True) new_sparse_map = np.full_like(self._sparse_map[key], _sentinel) - valid_indices = self._sparse_map[self._primary] != self._sentinel + valid_indices = (self._sparse_map[self._primary] != self._sentinel) new_sparse_map[valid_indices] = self._sparse_map[key][valid_indices] - return HealSparseMap( - cov_map=self._cov_map, - sparse_map=new_sparse_map, - nside_sparse=self._nside_sparse, - sentinel=_sentinel, - ) + return HealSparseMap(cov_map=self._cov_map, sparse_map=new_sparse_map, + nside_sparse=self._nside_sparse, sentinel=_sentinel) def get_single_covpix_map(self, covpix): """ @@ -2323,51 +2022,38 @@ def get_single_covpix_map(self, covpix): """ nfine_per_cov = self._cov_map._nfine_per_cov - if self._cov_map[covpix] + covpix * nfine_per_cov < nfine_per_cov: + if self._cov_map[covpix] + covpix*nfine_per_cov < nfine_per_cov: # Pixel is not in the coverage map; return an empty map return HealSparseMap.make_empty_like(self) - new_cov_map = HealSparseCoverage.make_from_pixels( - self.nside_coverage, self._nside_sparse, [covpix] - ) + new_cov_map = HealSparseCoverage.make_from_pixels(self.nside_coverage, + self._nside_sparse, + [covpix]) if self._is_wide_mask: - new_sparse_map = np.zeros( - (2 * nfine_per_cov, self._wide_mask_width), dtype=self.dtype - ) + new_sparse_map = np.zeros((2*nfine_per_cov, self._wide_mask_width), dtype=self.dtype) # Copy overflow bin - new_sparse_map[0:nfine_per_cov, :] = self._sparse_map[0:nfine_per_cov, :] + new_sparse_map[0: nfine_per_cov, :] = self._sparse_map[0: nfine_per_cov, :] # Copy the pixel - new_sparse_map[nfine_per_cov: 2 * nfine_per_cov, :] = self._sparse_map[ - self._cov_map[covpix] + covpix * nfine_per_cov: self._cov_map[covpix] + - covpix * nfine_per_cov + - nfine_per_cov, - :, - ] + new_sparse_map[nfine_per_cov: 2*nfine_per_cov, :] = self._sparse_map[ + self._cov_map[covpix] + covpix*nfine_per_cov: + self._cov_map[covpix] + covpix*nfine_per_cov + nfine_per_cov, :] elif self._is_bit_packed: - new_sparse_map = _PackedBoolArray(size=nfine_per_cov * 2) - new_sparse_map[nfine_per_cov: 2 * nfine_per_cov] = self._sparse_map[ - self._cov_map[covpix] + covpix * nfine_per_cov: self._cov_map[covpix] + - covpix * nfine_per_cov + - nfine_per_cov - ] + new_sparse_map = _PackedBoolArray(size=nfine_per_cov*2) + new_sparse_map[nfine_per_cov: 2*nfine_per_cov] = self._sparse_map[ + self._cov_map[covpix] + covpix*nfine_per_cov: + self._cov_map[covpix] + covpix*nfine_per_cov + nfine_per_cov] else: - new_sparse_map = np.zeros(2 * nfine_per_cov, dtype=self.dtype) + new_sparse_map = np.zeros(2*nfine_per_cov, dtype=self.dtype) # Copy overflow bin - new_sparse_map[0:nfine_per_cov] = self._sparse_map[0:nfine_per_cov] + new_sparse_map[0: nfine_per_cov] = self._sparse_map[0: nfine_per_cov] # Copy the pixel - new_sparse_map[nfine_per_cov: 2 * nfine_per_cov] = self._sparse_map[ - self._cov_map[covpix] + covpix * nfine_per_cov: self._cov_map[covpix] + - covpix * nfine_per_cov + - nfine_per_cov - ] + new_sparse_map[nfine_per_cov: 2*nfine_per_cov] = self._sparse_map[ + self._cov_map[covpix] + covpix*nfine_per_cov: + self._cov_map[covpix] + covpix*nfine_per_cov + nfine_per_cov] - return HealSparseMap( - cov_map=new_cov_map, - sparse_map=new_sparse_map, - nside_sparse=self._nside_sparse, - primary=self._primary, - sentinel=self._sentinel, - ) + return HealSparseMap(cov_map=new_cov_map, sparse_map=new_sparse_map, + nside_sparse=self._nside_sparse, primary=self._primary, + sentinel=self._sentinel) def get_covpix_maps(self): """ @@ -2386,7 +2072,7 @@ def get_covpix_maps(self): >>> for covpix_map in m.get_covpix_maps(): ... print(covpix_map.valid_pixels) """ - (cov_pixels,) = np.where(self._cov_map.coverage_mask) + cov_pixels, = np.where(self._cov_map.coverage_mask) for cov_pix in cov_pixels: yield self.get_single_covpix_map(cov_pix) @@ -2423,18 +2109,14 @@ def astype(self, dtype, sentinel=None): else: new_sparse_map = np.zeros(self._sparse_map.shape, dtype=dtype) - valid_pix = self._sparse_map != self._sentinel + valid_pix = (self._sparse_map != self._sentinel) new_sparse_map[valid_pix] = self._sparse_map[valid_pix].astype(dtype) _sentinel = check_sentinel(new_sparse_map.dtype.type, sentinel) new_sparse_map[~valid_pix] = _sentinel - return HealSparseMap( - cov_map=self._cov_map, - sparse_map=new_sparse_map, - nside_sparse=self.nside_sparse, - sentinel=_sentinel, - ) + return HealSparseMap(cov_map=self._cov_map, sparse_map=new_sparse_map, + nside_sparse=self.nside_sparse, sentinel=_sentinel) def as_bit_packed_map(self): """ @@ -2451,39 +2133,29 @@ def as_bit_packed_map(self): return self.copy() if (self._cov_map.nfine_per_cov % 8) != 0: - raise ValueError( - "Can only create a bit_packed mask map at least two " - "healpix levels between coverage and mask." - ) + raise ValueError("Can only create a bit_packed mask map at least two " + "healpix levels between coverage and mask.") # Need to go through coverage pixels, and copy the data into the new thing. # There is some fancy indexing that has to happen here. # The size will be the number of coverage pixels + 1 times nside - (coverage_pixels,) = np.where(self.coverage_mask) + coverage_pixels, = np.where(self.coverage_mask) n_cov = len(coverage_pixels) - bitmask_map = _PackedBoolArray(size=(n_cov + 1) * self._cov_map.nfine_per_cov) + bitmask_map = _PackedBoolArray(size=(n_cov + 1)*self._cov_map.nfine_per_cov) # This is the map without the offset. - cov_index_map_temp = ( - self._cov_map[:] + - np.arange( - hpg.nside_to_npixel(self._cov_map.nside_coverage), dtype=np.int64 - ) * - self._cov_map.nfine_per_cov - ) + cov_index_map_temp = self._cov_map[:] + np.arange(hpg.nside_to_npixel(self._cov_map.nside_coverage), + dtype=np.int64)*self._cov_map.nfine_per_cov for cov_pix in coverage_pixels: - s = slice( - cov_index_map_temp[cov_pix], - cov_index_map_temp[cov_pix] + self._cov_map.nfine_per_cov, - ) + s = slice(cov_index_map_temp[cov_pix], cov_index_map_temp[cov_pix] + self._cov_map.nfine_per_cov) if self._is_rec_array: - bool_data = self._sparse_map[self._primary][s] != self._sentinel + bool_data = (self._sparse_map[self._primary][s] != self._sentinel) elif self._is_wide_mask: bool_data = np.any(self._sparse_map[s] != self._sentinel, axis=1) else: - bool_data = self._sparse_map[s] != self._sentinel + bool_data = (self._sparse_map[s] != self._sentinel) # This is a bulk setter for aligned data. bitmask_map[s] = bool_data @@ -2644,9 +2316,7 @@ def __iand__(self, other): elif self.dtype == np.bool_: return self._apply_boolean_map_operation(other, "and", in_place=True) else: - return self._apply_operation( - other, np.bitwise_and, int_only=True, in_place=True - ) + return self._apply_operation(other, np.bitwise_and, int_only=True, in_place=True) def __xor__(self, other): """ @@ -2668,9 +2338,7 @@ def __ixor__(self, other): if self.dtype == np.bool_: return self._apply_boolean_map_operation(other, "xor", in_place=True) else: - return self._apply_operation( - other, np.bitwise_xor, int_only=True, in_place=True - ) + return self._apply_operation(other, np.bitwise_xor, int_only=True, in_place=True) def __or__(self, other): """ @@ -2715,21 +2383,18 @@ def __ior__(self, other): elif self.dtype == np.bool_: return self._apply_boolean_map_operation(other, "or", in_place=True) else: - return self._apply_operation( - other, np.bitwise_or, int_only=True, in_place=True - ) + return self._apply_operation(other, np.bitwise_or, int_only=True, in_place=True) def invert(self): - """Perform a bitwise inversion, over the coverage pixels, in place.""" + """Perform a bitwise inversion, over the coverage pixels, in place. + """ if self.dtype != np.bool_: raise NotImplementedError("Can only use invert(~) on boolean maps.") # We invalidate the n_valid cache here. self._n_valid = None - self._sparse_map[self._cov_map.nfine_per_cov:] = ~self._sparse_map[ - self._cov_map.nfine_per_cov: - ] + self._sparse_map[self._cov_map.nfine_per_cov:] = ~self._sparse_map[self._cov_map.nfine_per_cov:] return self def __invert__(self): @@ -2742,9 +2407,7 @@ def __invert__(self): raise NotImplementedError("Can only use invert(~) on boolean maps.") sparse_map_temp = self._sparse_map.copy() - sparse_map_temp[self._cov_map.nfine_per_cov:] = ~sparse_map_temp[ - self._cov_map.nfine_per_cov: - ] + sparse_map_temp[self._cov_map.nfine_per_cov:] = ~sparse_map_temp[self._cov_map.nfine_per_cov:] return HealSparseMap( cov_map=self._cov_map.copy(), sparse_map=sparse_map_temp, @@ -2802,46 +2465,32 @@ def _apply_operation(self, other, func, int_only=False, in_place=False): other_float = True elif isinstance(other, (tuple, list)): if not self._is_wide_mask: - raise NotImplementedError( - "Must use a wide mask to operate with a bit list" - ) + raise NotImplementedError("Must use a wide mask to operate with a bit list") other_bits = True for elt in other: if not isinstance(elt, numbers.Integral): - raise NotImplementedError( - "Can only use an integer list of bits " - "with %s operation" % (name) - ) + raise NotImplementedError("Can only use an integer list of bits " + "with %s operation" % (name)) if np.max(other) >= self._wide_mask_maxbits: - raise ValueError( - "Bit position %d too large (>= %d)" - % (np.max(other), self._wide_mask_maxbits) - ) + raise ValueError("Bit position %d too large (>= %d)" % (np.max(other), + self._wide_mask_maxbits)) if self._is_wide_mask: if not other_bits: - raise NotImplementedError( - "Must use a bit list with the %s operation with " - "a wide mask" % (name) - ) + raise NotImplementedError("Must use a bit list with the %s operation with " + "a wide mask" % (name)) else: if not other_int and not other_float: - raise NotImplementedError( - "Can only use a constant with the %s operation" % (name) - ) + raise NotImplementedError("Can only use a constant with the %s operation" % (name)) if not other_int and int_only: - raise NotImplementedError( - "Can only use an integer constant with the %s operation" % (name) - ) + raise NotImplementedError("Can only use an integer constant with the %s operation" % (name)) if self._is_wide_mask: - valid_sparse_pixels = (self._sparse_map != self._sentinel).sum( - axis=1, dtype=np.bool_ - ) + valid_sparse_pixels = (self._sparse_map != self._sentinel).sum(axis=1, dtype=np.bool_) other_value = _bitvals_to_packed_array(other, self._wide_mask_maxbits) else: - valid_sparse_pixels = self._sparse_map != self._sentinel + valid_sparse_pixels = (self._sparse_map != self._sentinel) if in_place: if self._is_wide_mask: @@ -2849,12 +2498,7 @@ def _apply_operation(self, other, func, int_only=False, in_place=False): col = self._sparse_map[:, i] func(col, other_value[i], out=col, where=valid_sparse_pixels) else: - func( - self._sparse_map, - other, - out=self._sparse_map, - where=valid_sparse_pixels, - ) + func(self._sparse_map, other, out=self._sparse_map, where=valid_sparse_pixels) return self else: combinedSparseMap = self._sparse_map.copy() @@ -2863,18 +2507,9 @@ def _apply_operation(self, other, func, int_only=False, in_place=False): col = combinedSparseMap[:, i] func(col, other_value[i], out=col, where=valid_sparse_pixels) else: - func( - combinedSparseMap, - other, - out=combinedSparseMap, - where=valid_sparse_pixels, - ) - return HealSparseMap( - cov_map=self._cov_map, - sparse_map=combinedSparseMap, - nside_sparse=self._nside_sparse, - sentinel=self._sentinel, - ) + func(combinedSparseMap, other, out=combinedSparseMap, where=valid_sparse_pixels) + return HealSparseMap(cov_map=self._cov_map, sparse_map=combinedSparseMap, + nside_sparse=self._nside_sparse, sentinel=self._sentinel) def _apply_boolean_map_operation(self, other, name, in_place=False): """Apply an operation to a boolean mask map. @@ -2894,9 +2529,7 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): Resulting map """ if name not in ("and", "or", "xor"): - raise NotImplementedError( - "_apply_boolean_map_operation does not support %s" % (name) - ) + raise NotImplementedError("_apply_boolean_map_operation does not support %s" % (name)) if in_place: # We invalidate the n_valid cache here. @@ -2923,66 +2556,48 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): elif isinstance(other, HealSparseMap): # Do an operation if these are allowed if not other.dtype == np.bool_: - raise NotImplementedError( - "Can only combine a boolean map with another boolean map." - ) + raise NotImplementedError("Can only combine a boolean map with another boolean map.") if self.nside_sparse != other.nside_sparse: - raise NotImplementedError( - "Boolean map operations only supported between maps with the " - "same nside_sparse." - ) + raise NotImplementedError("Boolean map operations only supported between maps with the " + "same nside_sparse.") if self.nside_coverage != other.nside_coverage: - raise NotImplementedError( - "Boolean map operations only supported between maps with the " - "same nside_coverage." - ) + raise NotImplementedError("Boolean map operations only supported between maps with the " + "same nside_coverage.") if self.sentinel or other.sentinel: - raise NotImplementedError( - "Boolean map operations only supported for maps with " - "False sentinel." - ) + raise NotImplementedError("Boolean map operations only supported for maps with " + "False sentinel.") # This routine will combine the coverage maps of the two masks. # We then loop over coverage pixels in the other map to do the # operation. coverage_mask = self.coverage_mask | other.coverage_mask - (cov_pixels_combined,) = coverage_mask.nonzero() - (cov_pixels_run,) = other.coverage_mask.nonzero() + cov_pixels_combined, = coverage_mask.nonzero() + cov_pixels_run, = other.coverage_mask.nonzero() - (new_cov_pix,) = (coverage_mask & ~self.coverage_mask).nonzero() + new_cov_pix, = (coverage_mask & ~self.coverage_mask).nonzero() if in_place: - (new_cov_pix,) = (coverage_mask & ~self.coverage_mask).nonzero() + new_cov_pix, = (coverage_mask & ~self.coverage_mask).nonzero() self._reserve_cov_pix(new_cov_pix) cov_map_temp = self._cov_map sparse_map_temp = self._sparse_map else: # Extend the coverage pixel map and copy data into new buffer. - cov_map_temp = self._cov_map.append_pixels( - len(self._sparse_map), new_cov_pix, check=False - ) - nsparse = (cov_pixels_combined.size + 1) * cov_map_temp.nfine_per_cov + cov_map_temp = self._cov_map.append_pixels(len(self._sparse_map), new_cov_pix, check=False) + nsparse = (cov_pixels_combined.size + 1)*cov_map_temp.nfine_per_cov if self._is_bit_packed: sparse_map_temp = _PackedBoolArray(size=nsparse) else: sparse_map_temp = np.zeros(nsparse, dtype=np.bool_) - sparse_map_temp[0: len(self._sparse_map)] = self._sparse_map[ - 0: len(self._sparse_map) - ] + sparse_map_temp[0: len(self._sparse_map)] = self._sparse_map[0: len(self._sparse_map)] for cov_pixel in cov_pixels_run: - start_self = ( - self._cov_map[cov_pixel] + cov_pixel * cov_map_temp.nfine_per_cov - ) + start_self = self._cov_map[cov_pixel] + cov_pixel*cov_map_temp.nfine_per_cov end_self = start_self + cov_map_temp.nfine_per_cov - start_other = ( - other._cov_map[cov_pixel] + cov_pixel * cov_map_temp.nfine_per_cov - ) + start_other = other._cov_map[cov_pixel] + cov_pixel*cov_map_temp.nfine_per_cov end_other = start_other + cov_map_temp.nfine_per_cov - start_temp = ( - cov_map_temp[cov_pixel] + cov_pixel * cov_map_temp.nfine_per_cov - ) + start_temp = cov_map_temp[cov_pixel] + cov_pixel*cov_map_temp.nfine_per_cov end_temp = start_temp + cov_map_temp.nfine_per_cov # The LHS will be guaranteed to have coverage (from above), @@ -2990,22 +2605,18 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): if not in_place: # Need to copy data. - sparse_map_temp[start_temp:end_temp] = self._sparse_map[ - start_self:end_self - ] + sparse_map_temp[start_temp: end_temp] = self._sparse_map[start_self: end_self] - lhs = sparse_map_temp[start_temp:end_temp] + lhs = sparse_map_temp[start_temp: end_temp] if self._is_bit_packed == other._is_bit_packed: # These match, no conversions necessary. - rhs = other._sparse_map[start_other:end_other] + rhs = other._sparse_map[start_other: end_other] elif self._is_bit_packed and not other._is_bit_packed: # Convert the RHS to a _PackedBoolArray. - rhs = _PackedBoolArray.from_boolean_array( - other._sparse_map[start_other:end_other] - ) + rhs = _PackedBoolArray.from_boolean_array(other._sparse_map[start_other: end_other]) elif not self._is_bit_packed and other._is_bit_packed: # Expand the RHS to a regular boolean array. - rhs = np.asarray(other._sparse_map[start_other:end_other]) + rhs = np.asarray(other._sparse_map[start_other: end_other]) if name == "and": lhs &= rhs @@ -3014,28 +2625,18 @@ def _apply_boolean_map_operation(self, other, name, in_place=False): elif name == "xor": lhs ^= rhs else: - raise NotImplementedError( - "Can only use a boolean or a boolean map with operation %s" % (name) - ) + raise NotImplementedError("Can only use a boolean or a boolean map with operation %s" % (name)) if in_place: return self else: - return HealSparseMap( - cov_map=cov_map_temp, - sparse_map=sparse_map_temp, - nside_sparse=self._nside_sparse, - sentinel=self._sentinel, - ) + return HealSparseMap(cov_map=cov_map_temp, sparse_map=sparse_map_temp, + nside_sparse=self._nside_sparse, sentinel=self._sentinel) def __copy__(self): - return HealSparseMap( - cov_map=self._cov_map.copy(), - sparse_map=self._sparse_map.copy(), - nside_sparse=self._nside_sparse, - sentinel=self._sentinel, - primary=self._primary, - ) + return HealSparseMap(cov_map=self._cov_map.copy(), + sparse_map=self._sparse_map.copy(), nside_sparse=self._nside_sparse, + sentinel=self._sentinel, primary=self._primary) def copy(self): return self.__copy__() @@ -3044,19 +2645,17 @@ def __repr__(self): return self.__str__() def __str__(self): - descr = "HealSparseMap: nside_coverage = %d, nside_sparse = %d" % ( - self.nside_coverage, - self._nside_sparse, - ) + descr = 'HealSparseMap: nside_coverage = %d, nside_sparse = %d' % (self.nside_coverage, + self._nside_sparse) if self._is_rec_array: - descr += ", record array type.\n" + descr += ', record array type.\n' descr += self._sparse_map.dtype.descr.__str__() elif self._is_wide_mask: - descr += ", %d bit wide mask" % (self._wide_mask_maxbits) + descr += ', %d bit wide mask' % (self._wide_mask_maxbits) elif self._is_bit_packed: - descr += ", boolean bit-packed mask" + descr += ', boolean bit-packed mask' else: - descr += ", " + self._sparse_map.dtype.name + descr += ', ' + self._sparse_map.dtype.name add_n_valid = True if self._is_bit_packed and self._n_valid is None: @@ -3066,6 +2665,6 @@ def __str__(self): add_n_valid = False if add_n_valid: - descr += ", %d valid pixels" % (self.n_valid) + descr += ', %d valid pixels' % (self.n_valid) return descr From 7a251e6e18ec676646fb23509c03a8a84c9d601b Mon Sep 17 00:00:00 2001 From: Patrick Wells Date: Wed, 20 May 2026 11:09:04 -0500 Subject: [PATCH 4/6] Update from_data with changes --- healsparse/healSparseMap.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/healsparse/healSparseMap.py b/healsparse/healSparseMap.py index c8a2490..f44f86e 100644 --- a/healsparse/healSparseMap.py +++ b/healsparse/healSparseMap.py @@ -320,7 +320,7 @@ def make_empty_like(cls, sparsemap, nside_coverage=None, nside_sparse=None, dtyp @classmethod def from_data( - pixels, data, nside_coverage, nside_sparse, nest=True, sentinel=hpg.UNSEEN + cls, pixels, data, nside_coverage, nside_sparse, nest=True, sentinel=hpg.UNSEEN ): """ Create a healsparse map from a partial healpix map. @@ -351,18 +351,9 @@ def from_data( pixels = np.where(full_map != sentinel)[0] data = full_map[pixels] - cov_map = HealSparseCoverage.make_empty(nside_coverage, nside_sparse) - cov_pix = cov_map.cov_pixels(pixels) - unique_cov_pix = np.unique(cov_pix) - - cov_map.initialize_pixels(unique_cov_pix) - sparse_indices = pixels + cov_map[cov_pix] - sparse_map_size = (len(unique_cov_pix) + 1) * cov_map.nfine_per_cov - - sparse_map = np.full(sparse_map_size, sentinel, dtype=data.dtype) - sparse_map[sparse_indices] = data - - return sparse_map + hsp_out = hsp.HealSparseMap.make_empty(nside_lr, nside, dtype=data.dtype, sentinel=sentinel) + hsp_out.update_values_pix(pixels, values, check_unique=False) + return hsp_out @staticmethod def convert_healpix_map(healpix_map, nside_coverage, nest=True, sentinel=hpg.UNSEEN): @@ -531,7 +522,7 @@ def update_values_pos(self, ra_or_theta, dec_or_phi, values, check_unique=check_unique, ) - def update_values_pix(self, pixels, values, nest=True, operation='replace'): + def update_values_pix(self, pixels, values, nest=True, operation='replace', check_unique=True): """ Update the values in the sparsemap for a list of pixels. The list of pixels must be unique if the operation is 'replace'. @@ -647,12 +638,10 @@ def update_values_pix(self, pixels, values, nest=True, operation='replace'): elif self._sparse_map.dtype.type != _values.dtype.type: raise ValueError("Data-type mismatch between sparse_map and values") - if operation == "replace" and check_unique: + if operation == 'replace' and check_unique: if hasattr(pixels, "__len__"): if has_duplicates(pixels): - raise ValueError( - "List of pixels must be unique if operation='replace'" - ) + raise ValueError("List of pixels must be unique if operation='replace'") if pixels.ndim == 2 and pixels.shape[1] == 2: # These are pixel ranges. From 06bbba7c0a9560f4b0c73cbef403208205dcb6d9 Mon Sep 17 00:00:00 2001 From: Patrick Wells Date: Wed, 20 May 2026 11:11:08 -0500 Subject: [PATCH 5/6] Fix lint error --- healsparse/healSparseMap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healsparse/healSparseMap.py b/healsparse/healSparseMap.py index f44f86e..194fb9c 100644 --- a/healsparse/healSparseMap.py +++ b/healsparse/healSparseMap.py @@ -8,7 +8,7 @@ from .utils import is_integer_value, _compute_bitshift from .io_map import _read_map, _write_map, _write_moc from .packedBoolArray import _PackedBoolArray -from .utils import fast_unique, has_duplicates, +from .utils import fast_unique, has_duplicates from .geom import GeomBase import warnings From 0882ccfead02985d53cb14b2e41a8c12fb9a60c0 Mon Sep 17 00:00:00 2001 From: Patrick Wells Date: Wed, 20 May 2026 11:13:12 -0500 Subject: [PATCH 6/6] Fix variable naming in from_data --- healsparse/healSparseMap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/healsparse/healSparseMap.py b/healsparse/healSparseMap.py index 194fb9c..cfc7695 100644 --- a/healsparse/healSparseMap.py +++ b/healsparse/healSparseMap.py @@ -351,8 +351,8 @@ def from_data( pixels = np.where(full_map != sentinel)[0] data = full_map[pixels] - hsp_out = hsp.HealSparseMap.make_empty(nside_lr, nside, dtype=data.dtype, sentinel=sentinel) - hsp_out.update_values_pix(pixels, values, check_unique=False) + hsp_out = HealSparseMap.make_empty(nside_coverage, nside_sparse, dtype=data.dtype, sentinel=sentinel) + hsp_out.update_values_pix(pixels, data, check_unique=False) return hsp_out @staticmethod