|
4 | 4 | from enum import IntEnum |
5 | 5 | from typing import TYPE_CHECKING |
6 | 6 |
|
| 7 | +import numpy as np |
| 8 | + |
7 | 9 | if TYPE_CHECKING: |
8 | 10 | import numpy as np |
9 | 11 |
|
@@ -67,7 +69,6 @@ def search(self, z: float, y: float, x: float, ei=None) -> dict[str, tuple[int, |
67 | 69 | """ |
68 | 70 | ... |
69 | 71 |
|
70 | | - @abstractmethod |
71 | 72 | def ravel_index(self, axis_indices: dict[str, int]) -> int: |
72 | 73 | """ |
73 | 74 | Convert a dictionary of axis indices to a single encoded index (ei). |
@@ -101,9 +102,10 @@ def ravel_index(self, axis_indices: dict[str, int]) -> int: |
101 | 102 | NotImplementedError |
102 | 103 | Raised if the method is not implemented for the current grid type. |
103 | 104 | """ |
104 | | - ... |
| 105 | + dims = np.array([self.get_axis_dim(axis) for axis in self.axes], dtype=int) |
| 106 | + indices = np.array([axis_indices[axis] for axis in self.axes], dtype=int) |
| 107 | + return _ravel(dims, indices) |
105 | 108 |
|
106 | | - @abstractmethod |
107 | 109 | def unravel_index(self, ei: int) -> dict[str, int]: |
108 | 110 | """ |
109 | 111 | Convert a single encoded index (ei) back to a dictionary of axis indices. |
@@ -134,4 +136,106 @@ def unravel_index(self, ei: int) -> dict[str, int]: |
134 | 136 | NotImplementedError |
135 | 137 | Raised if the method is not implemented for the current grid type. |
136 | 138 | """ |
| 139 | + dims = np.array([self.get_axis_dim(axis) for axis in self.axes], dtype=int) |
| 140 | + indices = _unravel(dims, ei) |
| 141 | + return dict(zip(self.axes, indices, strict=True)) |
| 142 | + |
| 143 | + @property |
| 144 | + @abstractmethod |
| 145 | + def axes(self) -> list[str]: |
| 146 | + """ |
| 147 | + Return a list of axis names that are part of this grid. |
| 148 | +
|
| 149 | + This list must at least be of length 1, and `get_axis_dim` should |
| 150 | + return a valid integer for each axis name in the list. |
| 151 | +
|
| 152 | + Returns |
| 153 | + ------- |
| 154 | + list[str] |
| 155 | + List of axis names, e.g. ["Z", "Y", "X"] for a 3D structured grid or ["Z", "FACE"] for an unstructured grid. |
| 156 | + """ |
| 157 | + ... |
| 158 | + |
| 159 | + @abstractmethod |
| 160 | + def get_axis_dim(self, axis: str) -> int: |
| 161 | + """ |
| 162 | + Return the dimensionality (number of cells/faces) along a specific axis. |
| 163 | +
|
| 164 | + Parameters |
| 165 | + ---------- |
| 166 | + axis : str |
| 167 | + The name of the axis to get the dimensionality for. Must be one of the values returned by self.axes. |
| 168 | +
|
| 169 | + Returns |
| 170 | + ------- |
| 171 | + int |
| 172 | + The number of cells/edges along the specified axis. |
| 173 | +
|
| 174 | + Raises |
| 175 | + ------ |
| 176 | + ValueError |
| 177 | + If the specified axis is not part of this grid. |
| 178 | + """ |
137 | 179 | ... |
| 180 | + |
| 181 | + |
| 182 | +def _unravel(dims, ei): |
| 183 | + """ |
| 184 | + Converts a flattened (raveled) index back to multi-dimensional indices. |
| 185 | +
|
| 186 | + Args: |
| 187 | + dims (1d-array-like): The dimensions along each axis |
| 188 | + ei (int): The flattened index to convert |
| 189 | +
|
| 190 | + Returns |
| 191 | + ------- |
| 192 | + array-like: Indices along each axis corresponding to the given flattened index |
| 193 | +
|
| 194 | + Example: |
| 195 | + >>> dims = [2, 3, 4] |
| 196 | + >>> ei = 9 |
| 197 | + >>> unravel(dims, ei) |
| 198 | + array([0, 2, 1]) |
| 199 | + # Calculation: |
| 200 | + # i0 = 9 // (3*4) = 9 // 12 = 0 |
| 201 | + # remainder = 9 % 12 = 9 |
| 202 | + # i1 = 9 // 4 = 2 |
| 203 | + # i2 = 9 % 4 = 1 |
| 204 | + """ |
| 205 | + strides = np.cumprod(dims[::-1])[::-1] |
| 206 | + |
| 207 | + indices = np.empty(len(dims), dtype=int) |
| 208 | + |
| 209 | + for i in range(len(dims) - 1): |
| 210 | + indices[i] = ei // strides[i + 1] |
| 211 | + ei = ei % strides[i + 1] |
| 212 | + |
| 213 | + indices[-1] = ei |
| 214 | + return indices |
| 215 | + |
| 216 | + |
| 217 | +def _ravel(dims, indices): |
| 218 | + """ |
| 219 | + Converts indices to a flattened (raveled) index. |
| 220 | +
|
| 221 | + Args: |
| 222 | + dims (1d-array-like): The dimensions along each axis |
| 223 | + indices (array-like): Indices along each axis to convert |
| 224 | +
|
| 225 | + Returns |
| 226 | + ------- |
| 227 | + int: The flattened index corresponding to the given indices |
| 228 | +
|
| 229 | + Example: |
| 230 | + >>> dims = [2, 3, 4] |
| 231 | + >>> indices = [0, 2, 1] |
| 232 | + >>> ravel(dims, indices) |
| 233 | + 9 |
| 234 | + # Calculation: 0 * (3 * 4) + 2 * (4) + 1 = 0 + 8 + 1 = 9 |
| 235 | + """ |
| 236 | + strides = np.cumprod(dims[::-1])[::-1] |
| 237 | + ei = 0 |
| 238 | + for i in range(len(dims) - 1): |
| 239 | + ei += indices[i] * strides[i + 1] |
| 240 | + |
| 241 | + return ei + indices[-1] |
0 commit comments