polytope-python 1.0.39__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 (122) hide show
  1. {polytope_python-1.0.39/polytope_python.egg-info → polytope_python-1.0.41}/PKG-INFO +1 -1
  2. {polytope_python-1.0.39 → 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/utility/__init__.py +0 -0
  5. polytope_python-1.0.41/polytope_feature/version.py +1 -0
  6. {polytope_python-1.0.39 → polytope_python-1.0.41/polytope_python.egg-info}/PKG-INFO +1 -1
  7. polytope_python-1.0.39/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -5
  8. polytope_python-1.0.39/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +0 -220
  9. polytope_python-1.0.39/polytope_feature/version.py +0 -1
  10. {polytope_python-1.0.39 → polytope_python-1.0.41}/LICENSE +0 -0
  11. {polytope_python-1.0.39 → polytope_python-1.0.41}/MANIFEST.in +0 -0
  12. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/__init__.py +0 -0
  13. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/__init__.py +0 -0
  14. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/backends/__init__.py +0 -0
  15. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/backends/datacube.py +0 -0
  16. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/backends/mock.py +0 -0
  17. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/backends/xarray.py +0 -0
  18. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/datacube_axis.py +0 -0
  19. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/index_tree_pb2.py +0 -0
  20. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/tensor_index_tree.py +0 -0
  21. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/__init__.py +0 -0
  22. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
  23. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
  24. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
  25. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +0 -0
  26. {polytope_python-1.0.39/polytope_feature/utility → polytope_python-1.0.41/polytope_feature/datacube/transformations/datacube_mappers/mapper_types}/__init__.py +0 -0
  27. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +0 -0
  28. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +0 -0
  29. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +0 -0
  30. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +0 -0
  31. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +0 -0
  32. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +0 -0
  33. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
  34. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
  35. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
  36. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
  37. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
  38. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
  39. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +0 -0
  40. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/datacube/tree_encoding.py +0 -0
  41. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/engine/__init__.py +0 -0
  42. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/engine/engine.py +0 -0
  43. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/engine/hullslicer.py +0 -0
  44. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/options.py +0 -0
  45. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/polytope.py +0 -0
  46. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/shapes.py +0 -0
  47. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/utility/combinatorics.py +0 -0
  48. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/utility/exceptions.py +0 -0
  49. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/utility/geometry.py +0 -0
  50. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/utility/list_tools.py +0 -0
  51. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_feature/utility/profiling.py +0 -0
  52. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_python.egg-info/SOURCES.txt +0 -0
  53. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_python.egg-info/dependency_links.txt +0 -0
  54. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_python.egg-info/not-zip-safe +0 -0
  55. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_python.egg-info/requires.txt +0 -0
  56. {polytope_python-1.0.39 → polytope_python-1.0.41}/polytope_python.egg-info/top_level.txt +0 -0
  57. {polytope_python-1.0.39 → polytope_python-1.0.41}/pyproject.toml +0 -0
  58. {polytope_python-1.0.39 → polytope_python-1.0.41}/requirements.txt +0 -0
  59. {polytope_python-1.0.39 → polytope_python-1.0.41}/setup.cfg +0 -0
  60. {polytope_python-1.0.39 → polytope_python-1.0.41}/setup.py +0 -0
  61. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_axis_mappers.py +0 -0
  62. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_bad_request_error.py +0 -0
  63. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_combinatorics.py +0 -0
  64. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_cyclic_axis_over_negative_vals.py +0 -0
  65. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_cyclic_axis_slicer_not_0.py +0 -0
  66. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_cyclic_axis_slicing.py +0 -0
  67. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_cyclic_nearest.py +0 -0
  68. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_cyclic_simple.py +0 -0
  69. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_cyclic_snapping.py +0 -0
  70. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_datacube_axes_init.py +0 -0
  71. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_datacube_mock.py +0 -0
  72. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_datacube_xarray.py +0 -0
  73. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_date_time_unmerged.py +0 -0
  74. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_ecmwf_oper_data_fdb.py +0 -0
  75. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_engine_slicer.py +0 -0
  76. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_fdb_datacube.py +0 -0
  77. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_fdb_unmap_tree.py +0 -0
  78. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_float_type.py +0 -0
  79. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_healpix_mapper.py +0 -0
  80. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_healpix_nested_grid.py +0 -0
  81. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_hull_slicer.py +0 -0
  82. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_hullslicer_engine.py +0 -0
  83. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_incomplete_tree_fdb.py +0 -0
  84. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_local_grid_cyclic.py +0 -0
  85. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_local_regular_grid.py +0 -0
  86. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_local_swiss_grid.py +0 -0
  87. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_mappers.py +0 -0
  88. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_merge_cyclic_octahedral.py +0 -0
  89. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_merge_octahedral_one_axis.py +0 -0
  90. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_merge_transformation.py +0 -0
  91. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_multiple_param_fdb.py +0 -0
  92. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_octahedral_grid.py +0 -0
  93. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_override_md5_hash_options.py +0 -0
  94. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_point_nearest.py +0 -0
  95. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_point_shape.py +0 -0
  96. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_point_union.py +0 -0
  97. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_profiling_requesttree.py +0 -0
  98. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_reduced_ll_grid.py +0 -0
  99. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_regular_grid.py +0 -0
  100. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_regular_reduced_grid.py +0 -0
  101. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_request_tree.py +0 -0
  102. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_request_trees_after_slicing.py +0 -0
  103. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_reverse_transformation.py +0 -0
  104. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_shapes.py +0 -0
  105. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slice_date_range_fdb.py +0 -0
  106. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slice_date_range_fdb_v2.py +0 -0
  107. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slice_fdb_box.py +0 -0
  108. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slicer_engine.py +0 -0
  109. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slicer_era5.py +0 -0
  110. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slicer_xarray.py +0 -0
  111. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slicing_unsliceable_axis.py +0 -0
  112. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slicing_xarray_3D.py +0 -0
  113. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_slicing_xarray_4D.py +0 -0
  114. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_snapping.py +0 -0
  115. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_snapping_real_data.py +0 -0
  116. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_tree_protobuf.py +0 -0
  117. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_tree_protobuf_encoding.py +0 -0
  118. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_tree_protobuf_encoding_fdb.py +0 -0
  119. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_type_change_transformation.py +0 -0
  120. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_union_gj.py +0 -0
  121. {polytope_python-1.0.39 → polytope_python-1.0.41}/tests/test_union_point_box.py +0 -0
  122. {polytope_python-1.0.39 → 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.39
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.39
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,5 +0,0 @@
1
- from .healpix import *
2
- from .local_regular import *
3
- from .octahedral import *
4
- from .reduced_ll import *
5
- from .regular import *
@@ -1,220 +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._cached_longitudes = {}
17
- self.k = int(math.log2(self.Nside))
18
- self.Npix = 12 * self.Nside * self.Nside
19
- self.Ncap = (self.Nside * (self.Nside - 1)) << 1
20
- self._healpix_longitudes = {}
21
- if md5_hash is not None:
22
- self.md5_hash = md5_hash
23
- else:
24
- self.md5_hash = _md5_hash.get(resolution, None)
25
- if self._axis_reversed[mapped_axes[1]]:
26
- raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported")
27
- if not self._axis_reversed[mapped_axes[0]]:
28
- raise NotImplementedError("Healpix grid with first axis in increasing order is not supported")
29
-
30
- def first_axis_vals(self):
31
- rad2deg = 180 / math.pi
32
- vals = [0] * (4 * self._resolution - 1)
33
-
34
- # Polar caps
35
- for i in range(1, self._resolution):
36
- val = 90 - (rad2deg * math.acos(1 - (i * i / (3 * self._resolution * self._resolution))))
37
- vals[i - 1] = val
38
- vals[4 * self._resolution - 1 - i] = -val
39
- # Equatorial belts
40
- for i in range(self._resolution, 2 * self._resolution):
41
- val = 90 - (rad2deg * math.acos((4 * self._resolution - 2 * i) / (3 * self._resolution)))
42
- vals[i - 1] = val
43
- vals[4 * self._resolution - 1 - i] = -val
44
- # Equator
45
- vals[2 * self._resolution - 1] = 0
46
- return vals
47
-
48
- def map_first_axis(self, lower, upper):
49
- axis_lines = self._first_axis_vals
50
- return_vals = [val for val in axis_lines if lower <= val <= upper]
51
- return return_vals
52
-
53
- def second_axis_vals(self, first_val):
54
- tol = 1e-8
55
- first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
56
- idx = self._first_axis_vals.index(first_val)
57
-
58
- values = self.HEALPix_longitudes(idx)
59
- return values
60
-
61
- def second_axis_vals_from_idx(self, first_val_idx):
62
- if first_val_idx not in self._healpix_longitudes:
63
- self._healpix_longitudes[first_val_idx] = self.HEALPix_longitudes(first_val_idx)
64
- values = self._healpix_longitudes[first_val_idx]
65
- return values
66
-
67
- def HEALPix_nj(self, i):
68
- assert self._resolution > 0
69
- ni = 4 * self._resolution - 1
70
- assert i < ni
71
-
72
- if i < self._resolution:
73
- return 4 * (i + 1)
74
- elif i < 3 * self._resolution:
75
- return 4 * self._resolution
76
- else:
77
- return self.HEALPix_nj(ni - 1 - i)
78
-
79
- def HEALPix_longitudes(self, i):
80
- if i in self._cached_longitudes:
81
- return self._cached_longitudes[i]
82
- else:
83
- Nj = self.HEALPix_nj(i)
84
- step = 360.0 / Nj
85
- start = (
86
- step / 2.0
87
- if i < self._resolution or 3 * self._resolution - 1 < i or (i + self._resolution) % 2
88
- else 0.0
89
- )
90
-
91
- longitudes = [start + n * step for n in range(Nj)]
92
- self._cached_longitudes[i] = longitudes
93
- return longitudes
94
-
95
- def map_second_axis(self, first_val, lower, upper):
96
- axis_lines = self.second_axis_vals(first_val)
97
- return_vals = [val for val in axis_lines if lower <= val <= upper]
98
- return return_vals
99
-
100
- def axes_idx_to_healpix_idx(self, first_idx, second_idx):
101
- idx = 0
102
- for i in range(self._resolution - 1):
103
- if i != first_idx:
104
- idx += 4 * (i + 1)
105
- else:
106
- idx += second_idx
107
- return idx
108
- for i in range(self._resolution - 1, 3 * self._resolution):
109
- if i != first_idx:
110
- idx += 4 * self._resolution
111
- else:
112
- idx += second_idx
113
- return idx
114
- for i in range(3 * self._resolution, 4 * self._resolution - 1):
115
- if i != first_idx:
116
- idx += 4 * (4 * self._resolution - 1 - i)
117
- else:
118
- idx += second_idx
119
- return idx
120
-
121
- def unmap(self, first_val, second_vals):
122
- tol = 1e-8
123
- first_idx = next(
124
- (i for i, val in enumerate(self._first_axis_vals) if first_val[0] - tol <= val <= first_val[0] + tol), None
125
- )
126
- if first_idx is None:
127
- return None
128
- second_axis_vals = self.second_axis_vals_from_idx(first_idx)
129
-
130
- return_idxs = []
131
- for second_val in second_vals:
132
- second_idx = next(
133
- (i for i, val in enumerate(second_axis_vals) if second_val - tol <= val <= second_val + tol), None
134
- )
135
- if second_idx is None:
136
- return None
137
- healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx)
138
- nested_healpix_index = self.ring_to_nested(healpix_index)
139
- return_idxs.append(nested_healpix_index)
140
- return return_idxs
141
-
142
- def div_03(self, a, b):
143
- t = 1 if a >= (b << 1) else 0
144
- a -= t * (b << 1)
145
- return (t << 1) + (1 if a >= b else 0)
146
-
147
- def pll(self, f):
148
- pll_values = [1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7]
149
- return pll_values[f]
150
-
151
- def to_nest(self, f, ring, Nring, phi, shift):
152
- r = int(((2 + (f >> 2)) << self.k) - ring - 1)
153
- p = int(2 * phi - self.pll(f) * Nring - shift - 1)
154
- if p >= 2 * self.Nside:
155
- p -= 8 * self.Nside
156
- i = int((r + p)) >> 1
157
- j = int((r - p)) >> 1
158
-
159
- return self.fij_to_nest(f, i, j, self.k)
160
-
161
- def fij_to_nest(self, f, i, j, k):
162
- return (f << (2 * k)) + self.nest_encode_bits(i) + (self.nest_encode_bits(j) << 1)
163
-
164
- def nest_encode_bits(self, i):
165
- __masks = [
166
- 0x00000000FFFFFFFF,
167
- 0x0000FFFF0000FFFF,
168
- 0x00FF00FF00FF00FF,
169
- 0x0F0F0F0F0F0F0F0F,
170
- 0x3333333333333333,
171
- 0x5555555555555555,
172
- ]
173
- i = int(i)
174
- b = i & __masks[0]
175
- b = (b ^ (b << 16)) & __masks[1]
176
- b = (b ^ (b << 8)) & __masks[2]
177
- b = (b ^ (b << 4)) & __masks[3]
178
- b = (b ^ (b << 2)) & __masks[4]
179
- b = (b ^ (b << 1)) & __masks[5]
180
- return b
181
-
182
- def ring_to_nested(self, idx):
183
- if idx < self.Ncap:
184
- # North polar cap
185
- Nring = (1 + self.int_sqrt(2 * idx + 1)) >> 1
186
- phi = 1 + idx - 2 * Nring * (Nring - 1)
187
- f = self.div_03(phi - 1, Nring)
188
- return self.to_nest(f, Nring, Nring, phi, 0)
189
-
190
- if self.Npix - self.Ncap <= idx:
191
- # South polar cap
192
- Nring = (1 + self.int_sqrt(2 * self.Npix - 2 * idx - 1)) >> 1
193
- phi = 1 + idx + 2 * Nring * (Nring - 1) + 4 * Nring - self.Npix
194
- ring = 4 * self.Nside - Nring # (from South pole)
195
- f = self.div_03(phi - 1, Nring) + 8
196
- return self.to_nest(f, ring, Nring, phi, 0)
197
- else:
198
- # Equatorial belt
199
- ip = idx - self.Ncap
200
- tmp = ip >> (self.k + 2)
201
-
202
- phi = ip - tmp * 4 * self.Nside + 1
203
- ring = tmp + self.Nside
204
-
205
- ifm = 1 + ((phi - 1 - ((1 + tmp) >> 1)) >> self.k)
206
- ifp = 1 + ((phi - 1 - ((1 - tmp + 2 * self.Nside) >> 1)) >> self.k)
207
- f = (ifp | 4) if ifp == ifm else (ifp if ifp < ifm else (ifm + 8))
208
-
209
- return self.to_nest(f, ring, self.Nside, phi, ring & 1)
210
-
211
- def int_sqrt(self, i):
212
- return int(math.sqrt(i + 0.5))
213
-
214
-
215
- # md5 grid hash in form {resolution : hash}
216
- _md5_hash = {
217
- 1024: "cbda19e48d4d7e5e22641154878b9b22",
218
- 512: "47efaa0853e70948a41d5225e7653194",
219
- 128: "f3dfeb7a5bbbdd13a20d10fdb3797c71",
220
- }
@@ -1 +0,0 @@
1
- __version__ = "1.0.39"