polytope-python 1.0.40__tar.gz → 1.0.41__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. {polytope_python-1.0.40/polytope_python.egg-info → polytope_python-1.0.41}/PKG-INFO +1 -1
  2. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/backends/fdb.py +2 -2
  3. polytope_python-1.0.41/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +218 -0
  4. polytope_python-1.0.41/polytope_feature/version.py +1 -0
  5. {polytope_python-1.0.40 → polytope_python-1.0.41/polytope_python.egg-info}/PKG-INFO +1 -1
  6. polytope_python-1.0.40/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +0 -210
  7. polytope_python-1.0.40/polytope_feature/version.py +0 -1
  8. {polytope_python-1.0.40 → polytope_python-1.0.41}/LICENSE +0 -0
  9. {polytope_python-1.0.40 → polytope_python-1.0.41}/MANIFEST.in +0 -0
  10. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/__init__.py +0 -0
  11. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/__init__.py +0 -0
  12. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/backends/__init__.py +0 -0
  13. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/backends/datacube.py +0 -0
  14. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/backends/mock.py +0 -0
  15. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/backends/xarray.py +0 -0
  16. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/datacube_axis.py +0 -0
  17. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/index_tree_pb2.py +0 -0
  18. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/tensor_index_tree.py +0 -0
  19. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/__init__.py +0 -0
  20. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
  21. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
  22. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
  23. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +0 -0
  24. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -0
  25. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +0 -0
  26. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +0 -0
  27. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +0 -0
  28. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +0 -0
  29. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +0 -0
  30. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +0 -0
  31. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
  32. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
  33. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
  34. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
  35. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
  36. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
  37. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +0 -0
  38. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/datacube/tree_encoding.py +0 -0
  39. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/engine/__init__.py +0 -0
  40. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/engine/engine.py +0 -0
  41. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/engine/hullslicer.py +0 -0
  42. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/options.py +0 -0
  43. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/polytope.py +0 -0
  44. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/shapes.py +0 -0
  45. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/utility/__init__.py +0 -0
  46. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/utility/combinatorics.py +0 -0
  47. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/utility/exceptions.py +0 -0
  48. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/utility/geometry.py +0 -0
  49. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/utility/list_tools.py +0 -0
  50. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_feature/utility/profiling.py +0 -0
  51. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_python.egg-info/SOURCES.txt +0 -0
  52. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_python.egg-info/dependency_links.txt +0 -0
  53. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_python.egg-info/not-zip-safe +0 -0
  54. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_python.egg-info/requires.txt +0 -0
  55. {polytope_python-1.0.40 → polytope_python-1.0.41}/polytope_python.egg-info/top_level.txt +0 -0
  56. {polytope_python-1.0.40 → polytope_python-1.0.41}/pyproject.toml +0 -0
  57. {polytope_python-1.0.40 → polytope_python-1.0.41}/requirements.txt +0 -0
  58. {polytope_python-1.0.40 → polytope_python-1.0.41}/setup.cfg +0 -0
  59. {polytope_python-1.0.40 → polytope_python-1.0.41}/setup.py +0 -0
  60. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_axis_mappers.py +0 -0
  61. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_bad_request_error.py +0 -0
  62. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_combinatorics.py +0 -0
  63. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_cyclic_axis_over_negative_vals.py +0 -0
  64. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_cyclic_axis_slicer_not_0.py +0 -0
  65. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_cyclic_axis_slicing.py +0 -0
  66. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_cyclic_nearest.py +0 -0
  67. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_cyclic_simple.py +0 -0
  68. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_cyclic_snapping.py +0 -0
  69. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_datacube_axes_init.py +0 -0
  70. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_datacube_mock.py +0 -0
  71. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_datacube_xarray.py +0 -0
  72. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_date_time_unmerged.py +0 -0
  73. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_ecmwf_oper_data_fdb.py +0 -0
  74. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_engine_slicer.py +0 -0
  75. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_fdb_datacube.py +0 -0
  76. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_fdb_unmap_tree.py +0 -0
  77. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_float_type.py +0 -0
  78. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_healpix_mapper.py +0 -0
  79. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_healpix_nested_grid.py +0 -0
  80. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_hull_slicer.py +0 -0
  81. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_hullslicer_engine.py +0 -0
  82. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_incomplete_tree_fdb.py +0 -0
  83. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_local_grid_cyclic.py +0 -0
  84. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_local_regular_grid.py +0 -0
  85. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_local_swiss_grid.py +0 -0
  86. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_mappers.py +0 -0
  87. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_merge_cyclic_octahedral.py +0 -0
  88. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_merge_octahedral_one_axis.py +0 -0
  89. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_merge_transformation.py +0 -0
  90. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_multiple_param_fdb.py +0 -0
  91. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_octahedral_grid.py +0 -0
  92. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_override_md5_hash_options.py +0 -0
  93. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_point_nearest.py +0 -0
  94. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_point_shape.py +0 -0
  95. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_point_union.py +0 -0
  96. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_profiling_requesttree.py +0 -0
  97. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_reduced_ll_grid.py +0 -0
  98. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_regular_grid.py +0 -0
  99. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_regular_reduced_grid.py +0 -0
  100. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_request_tree.py +0 -0
  101. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_request_trees_after_slicing.py +0 -0
  102. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_reverse_transformation.py +0 -0
  103. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_shapes.py +0 -0
  104. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slice_date_range_fdb.py +0 -0
  105. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slice_date_range_fdb_v2.py +0 -0
  106. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slice_fdb_box.py +0 -0
  107. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slicer_engine.py +0 -0
  108. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slicer_era5.py +0 -0
  109. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slicer_xarray.py +0 -0
  110. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slicing_unsliceable_axis.py +0 -0
  111. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slicing_xarray_3D.py +0 -0
  112. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_slicing_xarray_4D.py +0 -0
  113. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_snapping.py +0 -0
  114. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_snapping_real_data.py +0 -0
  115. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_tree_protobuf.py +0 -0
  116. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_tree_protobuf_encoding.py +0 -0
  117. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_tree_protobuf_encoding_fdb.py +0 -0
  118. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_type_change_transformation.py +0 -0
  119. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_union_gj.py +0 -0
  120. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_union_point_box.py +0 -0
  121. {polytope_python-1.0.40 → polytope_python-1.0.41}/tests/test_wave_spectra_data.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: polytope-python
3
- Version: 1.0.40
3
+ Version: 1.0.41
4
4
  Summary: Polytope datacube feature extraction library
5
5
  Home-page: https://github.com/ecmwf/polytope
6
6
  Author: ECMWF
@@ -205,11 +205,11 @@ class FDBDatacube(Datacube):
205
205
  original_fdb_node_range_vals = []
206
206
  new_current_start_idx = []
207
207
  for j, idx in enumerate(sub_lat_idxs):
208
- if idx not in seen_indices:
208
+ if idx.tolist() not in seen_indices:
209
209
  # NOTE: need to remove it from the values in the corresponding tree node
210
210
  # NOTE: need to read just the range we give to gj
211
211
  original_fdb_node_range_vals.append(actual_fdb_node[0].values[j])
212
- seen_indices.add(idx)
212
+ seen_indices.add(idx.tolist())
213
213
  new_current_start_idx.append(idx)
214
214
  if original_fdb_node_range_vals != []:
215
215
  actual_fdb_node[0].values = tuple(original_fdb_node_range_vals)
@@ -0,0 +1,218 @@
1
+ import math
2
+
3
+ import numpy as np
4
+
5
+ from ..datacube_mappers import DatacubeMapper
6
+
7
+
8
+ class NestedHealpixGridMapper(DatacubeMapper):
9
+ def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None):
10
+ # TODO: if local area is not empty list, raise NotImplemented
11
+ self._mapped_axes = mapped_axes
12
+ self._base_axis = base_axis
13
+ self._resolution = resolution
14
+ self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False}
15
+ self._first_axis_vals = self.first_axis_vals()
16
+ self._first_axis_vals_np_rounded = -np.round(np.array(self._first_axis_vals), decimals=8)
17
+ self.compressed_grid_axes = [self._mapped_axes[1]]
18
+ self.Nside = self._resolution
19
+ self.k = int(math.log2(self.Nside))
20
+ self.Npix = 12 * self.Nside * self.Nside
21
+ self.Ncap = (self.Nside * (self.Nside - 1)) << 1
22
+ if md5_hash is not None:
23
+ self.md5_hash = md5_hash
24
+ else:
25
+ self.md5_hash = _md5_hash.get(resolution, None)
26
+ if self._axis_reversed[mapped_axes[1]]:
27
+ raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported")
28
+ if not self._axis_reversed[mapped_axes[0]]:
29
+ raise NotImplementedError("Healpix grid with first axis in increasing order is not supported")
30
+
31
+ def first_axis_vals(self):
32
+ rad2deg = 180 / math.pi
33
+ vals = [0] * (4 * self._resolution - 1)
34
+
35
+ # Polar caps
36
+ for i in range(1, self._resolution):
37
+ val = 90 - (rad2deg * math.acos(1 - (i * i / (3 * self._resolution * self._resolution))))
38
+ vals[i - 1] = val
39
+ vals[4 * self._resolution - 1 - i] = -val
40
+ # Equatorial belts
41
+ for i in range(self._resolution, 2 * self._resolution):
42
+ val = 90 - (rad2deg * math.acos((4 * self._resolution - 2 * i) / (3 * self._resolution)))
43
+ vals[i - 1] = val
44
+ vals[4 * self._resolution - 1 - i] = -val
45
+ # Equator
46
+ vals[2 * self._resolution - 1] = 0
47
+ return vals
48
+
49
+ def map_first_axis(self, lower, upper):
50
+ axis_lines = self._first_axis_vals
51
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
52
+ return return_vals
53
+
54
+ def second_axis_vals(self, first_val):
55
+ tol = 1e-8
56
+ first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
57
+ idx = self._first_axis_vals.index(first_val)
58
+
59
+ values = self.HEALPix_longitudes(idx)
60
+ return values
61
+
62
+ def second_axis_vals_from_idx(self, first_val_idx):
63
+ values = self.HEALPix_longitudes(first_val_idx)
64
+ return values
65
+
66
+ def HEALPix_nj(self, i):
67
+ assert self._resolution > 0
68
+ ni = 4 * self._resolution - 1
69
+ assert i < ni
70
+
71
+ if i < self._resolution:
72
+ return 4 * (i + 1)
73
+ elif i < 3 * self._resolution:
74
+ return 4 * self._resolution
75
+ else:
76
+ return self.HEALPix_nj(ni - 1 - i)
77
+
78
+ def HEALPix_longitudes(self, i):
79
+ Nj = self.HEALPix_nj(i)
80
+ step = 360.0 / Nj
81
+ start = np.where(
82
+ (i < self._resolution) | (3 * self._resolution - 1 < i) | ((i + self._resolution) % 2 == 1), step / 2.0, 0.0
83
+ )
84
+ longitudes = start + np.arange(Nj) * step
85
+ return longitudes
86
+
87
+ def map_second_axis(self, first_val, lower, upper):
88
+ axis_lines = self.second_axis_vals(first_val)
89
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
90
+ return return_vals
91
+
92
+ def axes_idx_to_healpix_idx(self, first_idx, second_idx):
93
+ res = self._resolution
94
+ sum1 = 2 * (res - 1) * res
95
+ sum2 = 2 * (((res - 1) * res) - ((4 * res - 1 - first_idx) * (4 * res - first_idx)))
96
+
97
+ if first_idx < res - 1:
98
+ return (2 * first_idx * (first_idx + 1)) + second_idx
99
+ elif first_idx < 3 * res:
100
+ return sum1 + (first_idx - (res - 1)) * (4 * res) + second_idx
101
+ else:
102
+ return sum1 + (2 * res + 1) * (4 * res) + sum2 + second_idx
103
+
104
+ def unmap(self, first_val, second_vals):
105
+ # Convert to NumPy array for fast computation
106
+ idx = np.searchsorted(self._first_axis_vals_np_rounded, -np.round(first_val[0], decimals=8))
107
+ if idx >= len(self._first_axis_vals_np_rounded):
108
+ return None
109
+ second_axis_vals = np.round(np.array(self.second_axis_vals_from_idx(idx)), decimals=8)
110
+ second_vals = np.round(np.array(second_vals), decimals=8)
111
+ second_idxs = np.searchsorted(second_axis_vals, second_vals)
112
+ valid_mask = second_idxs < len(second_axis_vals)
113
+ if not np.all(valid_mask):
114
+ return None
115
+ healpix_idxs = [self.axes_idx_to_healpix_idx(idx, sec_idx) for sec_idx in second_idxs]
116
+ return self.ring_to_nested(np.asarray(healpix_idxs))
117
+
118
+ def div_03(self, a, b):
119
+ """Vectorized version of div_03"""
120
+ t = np.where(a >= (b << 1), 1, 0)
121
+ a -= t * (b << 1)
122
+ return (t << 1) + np.where(a >= b, 1, 0)
123
+
124
+ def pll(self, f):
125
+ """Vectorized lookup for PLL values"""
126
+ pll_values = np.array([1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7])
127
+ return pll_values[f]
128
+
129
+ def to_nest(self, f, ring, Nring, phi, shift):
130
+ """Vectorized to_nest conversion"""
131
+ r = ((2 + (f >> 2)) << self.k) - ring - 1
132
+ p = 2 * phi - self.pll(f) * Nring - shift - 1
133
+ p = np.where(p >= 2 * self.Nside, p - 8 * self.Nside, p)
134
+
135
+ i = (r + p) >> 1
136
+ j = (r - p) >> 1
137
+ return self.fij_to_nest(f, i, j, self.k)
138
+
139
+ def fij_to_nest(self, f, i, j, k):
140
+ """Vectorized nest encoding"""
141
+ return (
142
+ # (f.astype(np.uint64) << np.uint64(2 * k))
143
+ (f.astype(object) << (2 * k))
144
+ + self.nest_encode_bits(i)
145
+ + (self.nest_encode_bits(j).astype(np.uint64) << np.uint64(1))
146
+ )
147
+
148
+ def nest_encode_bits(self, i):
149
+ """Vectorized bit manipulation for HEALPix indexing"""
150
+ __masks = np.array(
151
+ [
152
+ 0x00000000FFFFFFFF,
153
+ 0x0000FFFF0000FFFF,
154
+ 0x00FF00FF00FF00FF,
155
+ 0x0F0F0F0F0F0F0F0F,
156
+ 0x3333333333333333,
157
+ 0x5555555555555555,
158
+ ],
159
+ dtype=np.uint64,
160
+ )
161
+
162
+ b = i.astype(np.uint64) & __masks[0]
163
+ b = (b ^ (b << np.uint64(16))) & __masks[1]
164
+ b = (b ^ (b << np.uint64(8))) & __masks[2]
165
+ b = (b ^ (b << np.uint64(4))) & __masks[3]
166
+ b = (b ^ (b << np.uint64(2))) & __masks[4]
167
+ b = (b ^ (b << np.uint64(1))) & __masks[5]
168
+ return b
169
+
170
+ def int_sqrt(self, x):
171
+ """Efficient integer square root for arrays"""
172
+ return np.sqrt(x + 0.5).astype(int)
173
+
174
+ def ring_to_nested(self, idx):
175
+ """Vectorized ring_to_nested conversion"""
176
+ # idx = np.asarray(idx) # Ensure input is an array
177
+
178
+ north_mask = idx < self.Ncap
179
+ south_mask = self.Npix - self.Ncap <= idx
180
+
181
+ # North polar cap
182
+ Nring_north = (1 + self.int_sqrt(2 * idx + 1)) >> 1
183
+ phi_north = 1 + idx - 2 * Nring_north * (Nring_north - 1)
184
+ f_north = self.div_03(phi_north - 1, Nring_north)
185
+ nested_north = self.to_nest(f_north, Nring_north, Nring_north, phi_north, 0)
186
+
187
+ # South polar cap
188
+ Nring_south = (1 + self.int_sqrt(2 * self.Npix - 2 * idx - 1)) >> 1
189
+ phi_south = 1 + idx + 2 * Nring_south * (Nring_south - 1) + 4 * Nring_south - self.Npix
190
+ ring_south = 4 * self.Nside - Nring_south
191
+ f_south = self.div_03(phi_south - 1, Nring_south) + 8
192
+ nested_south = self.to_nest(f_south, ring_south, Nring_south, phi_south, 0)
193
+
194
+ # Equatorial belt
195
+ ip = idx - self.Ncap
196
+ tmp = ip >> (self.k + 2)
197
+
198
+ phi_equatorial = ip - tmp * 4 * self.Nside + 1
199
+ ring_equatorial = tmp + self.Nside
200
+
201
+ ifm = 1 + ((phi_equatorial - 1 - ((1 + tmp) >> 1)) >> self.k)
202
+ ifp = 1 + ((phi_equatorial - 1 - ((1 - tmp + 2 * self.Nside) >> 1)) >> self.k)
203
+ f_equatorial = np.where(ifp == ifm, ifp | 4, np.where(ifp < ifm, ifp, ifm + 8))
204
+
205
+ nested_equatorial = self.to_nest(f_equatorial, ring_equatorial, self.Nside, phi_equatorial, ring_equatorial & 1)
206
+ nested_result = np.empty_like(idx) # Preallocate array for performance
207
+ nested_result[north_mask] = nested_north[north_mask]
208
+ nested_result[south_mask] = nested_south[south_mask]
209
+ nested_result[~(north_mask | south_mask)] = nested_equatorial[~(north_mask | south_mask)]
210
+ return nested_result
211
+
212
+
213
+ # md5 grid hash in form {resolution : hash}
214
+ _md5_hash = {
215
+ 1024: "cbda19e48d4d7e5e22641154878b9b22",
216
+ 512: "47efaa0853e70948a41d5225e7653194",
217
+ 128: "f3dfeb7a5bbbdd13a20d10fdb3797c71",
218
+ }
@@ -0,0 +1 @@
1
+ __version__ = "1.0.41"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: polytope-python
3
- Version: 1.0.40
3
+ Version: 1.0.41
4
4
  Summary: Polytope datacube feature extraction library
5
5
  Home-page: https://github.com/ecmwf/polytope
6
6
  Author: ECMWF
@@ -1,210 +0,0 @@
1
- import math
2
-
3
- from ..datacube_mappers import DatacubeMapper
4
-
5
-
6
- class NestedHealpixGridMapper(DatacubeMapper):
7
- def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None):
8
- # TODO: if local area is not empty list, raise NotImplemented
9
- self._mapped_axes = mapped_axes
10
- self._base_axis = base_axis
11
- self._resolution = resolution
12
- self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False}
13
- self._first_axis_vals = self.first_axis_vals()
14
- self.compressed_grid_axes = [self._mapped_axes[1]]
15
- self.Nside = self._resolution
16
- self.k = int(math.log2(self.Nside))
17
- self.Npix = 12 * self.Nside * self.Nside
18
- self.Ncap = (self.Nside * (self.Nside - 1)) << 1
19
- if md5_hash is not None:
20
- self.md5_hash = md5_hash
21
- else:
22
- self.md5_hash = _md5_hash.get(resolution, None)
23
- if self._axis_reversed[mapped_axes[1]]:
24
- raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported")
25
- if not self._axis_reversed[mapped_axes[0]]:
26
- raise NotImplementedError("Healpix grid with first axis in increasing order is not supported")
27
-
28
- def first_axis_vals(self):
29
- rad2deg = 180 / math.pi
30
- vals = [0] * (4 * self._resolution - 1)
31
-
32
- # Polar caps
33
- for i in range(1, self._resolution):
34
- val = 90 - (rad2deg * math.acos(1 - (i * i / (3 * self._resolution * self._resolution))))
35
- vals[i - 1] = val
36
- vals[4 * self._resolution - 1 - i] = -val
37
- # Equatorial belts
38
- for i in range(self._resolution, 2 * self._resolution):
39
- val = 90 - (rad2deg * math.acos((4 * self._resolution - 2 * i) / (3 * self._resolution)))
40
- vals[i - 1] = val
41
- vals[4 * self._resolution - 1 - i] = -val
42
- # Equator
43
- vals[2 * self._resolution - 1] = 0
44
- return vals
45
-
46
- def map_first_axis(self, lower, upper):
47
- axis_lines = self._first_axis_vals
48
- return_vals = [val for val in axis_lines if lower <= val <= upper]
49
- return return_vals
50
-
51
- def second_axis_vals(self, first_val):
52
- tol = 1e-8
53
- first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
54
- idx = self._first_axis_vals.index(first_val)
55
-
56
- values = self.HEALPix_longitudes(idx)
57
- return values
58
-
59
- def second_axis_vals_from_idx(self, first_val_idx):
60
- values = self.HEALPix_longitudes(first_val_idx)
61
- return values
62
-
63
- def HEALPix_nj(self, i):
64
- assert self._resolution > 0
65
- ni = 4 * self._resolution - 1
66
- assert i < ni
67
-
68
- if i < self._resolution:
69
- return 4 * (i + 1)
70
- elif i < 3 * self._resolution:
71
- return 4 * self._resolution
72
- else:
73
- return self.HEALPix_nj(ni - 1 - i)
74
-
75
- def HEALPix_longitudes(self, i):
76
- Nj = self.HEALPix_nj(i)
77
- step = 360.0 / Nj
78
- start = (
79
- step / 2.0 if i < self._resolution or 3 * self._resolution - 1 < i or (i + self._resolution) % 2 else 0.0
80
- )
81
-
82
- longitudes = [start + n * step for n in range(Nj)]
83
- return longitudes
84
-
85
- def map_second_axis(self, first_val, lower, upper):
86
- axis_lines = self.second_axis_vals(first_val)
87
- return_vals = [val for val in axis_lines if lower <= val <= upper]
88
- return return_vals
89
-
90
- def axes_idx_to_healpix_idx(self, first_idx, second_idx):
91
- idx = 0
92
- for i in range(self._resolution - 1):
93
- if i != first_idx:
94
- idx += 4 * (i + 1)
95
- else:
96
- idx += second_idx
97
- return idx
98
- for i in range(self._resolution - 1, 3 * self._resolution):
99
- if i != first_idx:
100
- idx += 4 * self._resolution
101
- else:
102
- idx += second_idx
103
- return idx
104
- for i in range(3 * self._resolution, 4 * self._resolution - 1):
105
- if i != first_idx:
106
- idx += 4 * (4 * self._resolution - 1 - i)
107
- else:
108
- idx += second_idx
109
- return idx
110
-
111
- def unmap(self, first_val, second_vals):
112
- tol = 1e-8
113
- first_idx = next(
114
- (i for i, val in enumerate(self._first_axis_vals) if first_val[0] - tol <= val <= first_val[0] + tol), None
115
- )
116
- if first_idx is None:
117
- return None
118
- second_axis_vals = self.second_axis_vals_from_idx(first_idx)
119
-
120
- return_idxs = []
121
- for second_val in second_vals:
122
- second_idx = next(
123
- (i for i, val in enumerate(second_axis_vals) if second_val - tol <= val <= second_val + tol), None
124
- )
125
- if second_idx is None:
126
- return None
127
- healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx)
128
- nested_healpix_index = self.ring_to_nested(healpix_index)
129
- return_idxs.append(nested_healpix_index)
130
- return return_idxs
131
-
132
- def div_03(self, a, b):
133
- t = 1 if a >= (b << 1) else 0
134
- a -= t * (b << 1)
135
- return (t << 1) + (1 if a >= b else 0)
136
-
137
- def pll(self, f):
138
- pll_values = [1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7]
139
- return pll_values[f]
140
-
141
- def to_nest(self, f, ring, Nring, phi, shift):
142
- r = int(((2 + (f >> 2)) << self.k) - ring - 1)
143
- p = int(2 * phi - self.pll(f) * Nring - shift - 1)
144
- if p >= 2 * self.Nside:
145
- p -= 8 * self.Nside
146
- i = int((r + p)) >> 1
147
- j = int((r - p)) >> 1
148
-
149
- return self.fij_to_nest(f, i, j, self.k)
150
-
151
- def fij_to_nest(self, f, i, j, k):
152
- return (f << (2 * k)) + self.nest_encode_bits(i) + (self.nest_encode_bits(j) << 1)
153
-
154
- def nest_encode_bits(self, i):
155
- __masks = [
156
- 0x00000000FFFFFFFF,
157
- 0x0000FFFF0000FFFF,
158
- 0x00FF00FF00FF00FF,
159
- 0x0F0F0F0F0F0F0F0F,
160
- 0x3333333333333333,
161
- 0x5555555555555555,
162
- ]
163
- i = int(i)
164
- b = i & __masks[0]
165
- b = (b ^ (b << 16)) & __masks[1]
166
- b = (b ^ (b << 8)) & __masks[2]
167
- b = (b ^ (b << 4)) & __masks[3]
168
- b = (b ^ (b << 2)) & __masks[4]
169
- b = (b ^ (b << 1)) & __masks[5]
170
- return b
171
-
172
- def ring_to_nested(self, idx):
173
- if idx < self.Ncap:
174
- # North polar cap
175
- Nring = (1 + self.int_sqrt(2 * idx + 1)) >> 1
176
- phi = 1 + idx - 2 * Nring * (Nring - 1)
177
- f = self.div_03(phi - 1, Nring)
178
- return self.to_nest(f, Nring, Nring, phi, 0)
179
-
180
- if self.Npix - self.Ncap <= idx:
181
- # South polar cap
182
- Nring = (1 + self.int_sqrt(2 * self.Npix - 2 * idx - 1)) >> 1
183
- phi = 1 + idx + 2 * Nring * (Nring - 1) + 4 * Nring - self.Npix
184
- ring = 4 * self.Nside - Nring # (from South pole)
185
- f = self.div_03(phi - 1, Nring) + 8
186
- return self.to_nest(f, ring, Nring, phi, 0)
187
- else:
188
- # Equatorial belt
189
- ip = idx - self.Ncap
190
- tmp = ip >> (self.k + 2)
191
-
192
- phi = ip - tmp * 4 * self.Nside + 1
193
- ring = tmp + self.Nside
194
-
195
- ifm = 1 + ((phi - 1 - ((1 + tmp) >> 1)) >> self.k)
196
- ifp = 1 + ((phi - 1 - ((1 - tmp + 2 * self.Nside) >> 1)) >> self.k)
197
- f = (ifp | 4) if ifp == ifm else (ifp if ifp < ifm else (ifm + 8))
198
-
199
- return self.to_nest(f, ring, self.Nside, phi, ring & 1)
200
-
201
- def int_sqrt(self, i):
202
- return int(math.sqrt(i + 0.5))
203
-
204
-
205
- # md5 grid hash in form {resolution : hash}
206
- _md5_hash = {
207
- 1024: "cbda19e48d4d7e5e22641154878b9b22",
208
- 512: "47efaa0853e70948a41d5225e7653194",
209
- 128: "f3dfeb7a5bbbdd13a20d10fdb3797c71",
210
- }
@@ -1 +0,0 @@
1
- __version__ = "1.0.40"