simind-python-connector 1.1.2__py3-none-any.whl → 1.2.0__py3-none-any.whl

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 (35) hide show
  1. simind_python_connector/__init__.py +2 -11
  2. simind_python_connector/_version.py +3 -0
  3. simind_python_connector/backends/__init__.py +30 -28
  4. simind_python_connector/backends/base.py +7 -52
  5. simind_python_connector/backends/sirf_backend.py +19 -23
  6. simind_python_connector/backends/stir_backend.py +38 -29
  7. simind_python_connector/builders/__init__.py +2 -2
  8. simind_python_connector/builders/acquisition_builder.py +8 -25
  9. simind_python_connector/builders/image_builder.py +4 -4
  10. simind_python_connector/configs/__init__.py +1 -2
  11. simind_python_connector/connectors/__init__.py +2 -2
  12. simind_python_connector/connectors/_spacing.py +4 -16
  13. simind_python_connector/connectors/base.py +35 -2
  14. simind_python_connector/connectors/python_connector.py +40 -25
  15. simind_python_connector/connectors/pytomography_adaptor.py +28 -37
  16. simind_python_connector/connectors/sirf_adaptor.py +10 -24
  17. simind_python_connector/connectors/stir_adaptor.py +13 -24
  18. simind_python_connector/converters/attenuation.py +1 -10
  19. simind_python_connector/converters/simind_to_stir.py +32 -32
  20. simind_python_connector/core/config.py +1 -0
  21. simind_python_connector/core/executor.py +9 -10
  22. simind_python_connector/utils/__init__.py +7 -17
  23. simind_python_connector/utils/backend_access.py +4 -4
  24. simind_python_connector/utils/import_helpers.py +4 -5
  25. simind_python_connector/utils/interfile_numpy.py +11 -4
  26. simind_python_connector/utils/interfile_parser.py +9 -10
  27. simind_python_connector/utils/simind_utils.py +0 -7
  28. simind_python_connector/utils/sirf_stir_utils.py +11 -11
  29. simind_python_connector/utils/stir_utils.py +12 -18
  30. {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/METADATA +4 -1
  31. simind_python_connector-1.2.0.dist-info/RECORD +49 -0
  32. simind_python_connector-1.1.2.dist-info/RECORD +0 -48
  33. {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/WHEEL +0 -0
  34. {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/licenses/LICENSE +0 -0
  35. {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/top_level.txt +0 -0
@@ -3,18 +3,9 @@ simind-python-connector connector/adaptor API.
3
3
  """
4
4
 
5
5
  import importlib
6
- from importlib import metadata as _meta
7
6
  from typing import Any
8
7
 
9
-
10
- for _dist_name in ("simind-python-connector", __name__):
11
- try: # installed (pip/poetry)
12
- __version__ = _meta.version(_dist_name)
13
- break
14
- except _meta.PackageNotFoundError:
15
- continue
16
- else: # editable / source checkout
17
- __version__ = "1.1.2"
8
+ from ._version import __version__ as __version__
18
9
 
19
10
 
20
11
  def __getattr__(name: str) -> Any:
@@ -58,9 +49,9 @@ __all__ = [
58
49
  "ProjectionResult",
59
50
  "PyTomographySimindAdaptor",
60
51
  "RuntimeOperator",
61
- "SirfSimindAdaptor",
62
52
  "SimindPythonConnector",
63
53
  "SimulationConfig",
54
+ "SirfSimindAdaptor",
64
55
  "StirSimindAdaptor",
65
56
  "builders",
66
57
  "configs",
@@ -0,0 +1,3 @@
1
+ """Canonical package version, also read by packaging and release tooling."""
2
+
3
+ __version__ = "1.2.0"
@@ -20,7 +20,7 @@ Usage:
20
20
  import importlib
21
21
  import logging
22
22
  import os
23
- from typing import TYPE_CHECKING, Any, Literal, Optional, Union
23
+ from typing import TYPE_CHECKING, Any, Literal, Union
24
24
 
25
25
  from ..utils.import_helpers import _import_stir_extra
26
26
  from .base import AcquisitionDataInterface, ImageDataInterface
@@ -44,11 +44,11 @@ if TYPE_CHECKING:
44
44
  FloatVoxelsOnCartesianGrid = Any # type: ignore[assignment]
45
45
 
46
46
  # Global backend state
47
- _backend: Optional[str] = None
47
+ _backend: str | None = None
48
48
  _backend_initialized = False
49
49
 
50
50
 
51
- def _import_sirf_types() -> tuple[Optional[type], Optional[type]]:
51
+ def _import_sirf_types() -> tuple[type | None, type | None]:
52
52
  """Import SIRF image/acquisition types lazily."""
53
53
  try:
54
54
  from sirf.STIR import AcquisitionData as SirfAcquisitionData # type: ignore
@@ -58,7 +58,7 @@ def _import_sirf_types() -> tuple[Optional[type], Optional[type]]:
58
58
  return SirfImageData, SirfAcquisitionData
59
59
 
60
60
 
61
- def _import_stir_types() -> tuple[Optional[type], Optional[type]]:
61
+ def _import_stir_types() -> tuple[type | None, type | None]:
62
62
  """Import STIR image/acquisition types lazily."""
63
63
  try:
64
64
  from stir import FloatVoxelsOnCartesianGrid as StirImageData # type: ignore
@@ -69,7 +69,7 @@ def _import_stir_types() -> tuple[Optional[type], Optional[type]]:
69
69
  return StirImageData, StirProjData
70
70
 
71
71
 
72
- def detect_image_backend(obj: Any) -> Optional[str]:
72
+ def detect_image_backend(obj: Any) -> str | None:
73
73
  """Return 'sirf' or 'stir' if obj is a native image type."""
74
74
  if obj is None:
75
75
  return None
@@ -85,7 +85,7 @@ def detect_image_backend(obj: Any) -> Optional[str]:
85
85
  return None
86
86
 
87
87
 
88
- def detect_acquisition_backend(obj: Any) -> Optional[str]:
88
+ def detect_acquisition_backend(obj: Any) -> str | None:
89
89
  """Return 'sirf' or 'stir' if obj is a native acquisition type."""
90
90
  if obj is None:
91
91
  return None
@@ -102,8 +102,8 @@ def detect_acquisition_backend(obj: Any) -> Optional[str]:
102
102
 
103
103
 
104
104
  def detect_backend_from_interface(
105
- obj: Union[ImageDataInterface, AcquisitionDataInterface, Any],
106
- ) -> Optional[str]:
105
+ obj: ImageDataInterface | AcquisitionDataInterface | Any,
106
+ ) -> str | None:
107
107
  """Detect backend by unwrapping known interface objects."""
108
108
  native = getattr(obj, "native_object", None)
109
109
  if native is None:
@@ -116,7 +116,7 @@ def detect_backend_from_interface(
116
116
  return detect_acquisition_backend(native)
117
117
 
118
118
 
119
- def _activate_backend(preferred: Optional[str]) -> Optional[str]:
119
+ def _activate_backend(preferred: str | None) -> str | None:
120
120
  """Force-activate a specific backend if requested."""
121
121
  if preferred is None:
122
122
  return None
@@ -211,9 +211,10 @@ def reset_backend() -> None:
211
211
 
212
212
 
213
213
  def create_image_data(
214
- filepath_or_object: Optional[
215
- Union[str, ImageDataInterface, "ImageData", "FloatVoxelsOnCartesianGrid"]
216
- ] = None,
214
+ filepath_or_object: Union[
215
+ str, ImageDataInterface, "ImageData", "FloatVoxelsOnCartesianGrid"
216
+ ]
217
+ | None = None,
217
218
  ) -> ImageDataInterface:
218
219
  """Create an image data object using the current backend.
219
220
 
@@ -295,9 +296,10 @@ def create_image_data(
295
296
 
296
297
 
297
298
  def create_acquisition_data(
298
- filepath_or_object: Optional[
299
- Union[str, AcquisitionDataInterface, "AcquisitionData", "ProjData"]
300
- ] = None,
299
+ filepath_or_object: Union[
300
+ str, AcquisitionDataInterface, "AcquisitionData", "ProjData"
301
+ ]
302
+ | None = None,
301
303
  ) -> AcquisitionDataInterface:
302
304
  """Create an acquisition data object using the current backend.
303
305
 
@@ -380,7 +382,7 @@ def create_acquisition_data(
380
382
 
381
383
 
382
384
  def load_image_data(
383
- filepath: str, backend: Optional[Literal["sirf", "stir"]] = None
385
+ filepath: str, backend: Literal["sirf", "stir"] | None = None
384
386
  ) -> ImageDataInterface:
385
387
  """Load image data from file for the requested backend.
386
388
 
@@ -409,7 +411,7 @@ def load_image_data(
409
411
 
410
412
 
411
413
  def load_acquisition_data(
412
- filepath: str, backend: Optional[Literal["sirf", "stir"]] = None
414
+ filepath: str, backend: Literal["sirf", "stir"] | None = None
413
415
  ) -> AcquisitionDataInterface:
414
416
  """Load acquisition data from file for the requested backend.
415
417
 
@@ -455,7 +457,7 @@ def is_stir_backend() -> bool:
455
457
  return get_backend() == "stir"
456
458
 
457
459
 
458
- def unwrap(obj: Union[ImageDataInterface, AcquisitionDataInterface]):
460
+ def unwrap(obj: ImageDataInterface | AcquisitionDataInterface):
459
461
  """Get the native object from a wrapped backend object.
460
462
 
461
463
  Args:
@@ -470,19 +472,19 @@ def unwrap(obj: Union[ImageDataInterface, AcquisitionDataInterface]):
470
472
 
471
473
 
472
474
  __all__ = [
473
- "get_backend",
474
- "set_backend",
475
- "reset_backend",
476
- "detect_image_backend",
475
+ "AcquisitionDataInterface",
476
+ "ImageDataInterface",
477
+ "create_acquisition_data",
478
+ "create_image_data",
477
479
  "detect_acquisition_backend",
478
480
  "detect_backend_from_interface",
479
- "create_image_data",
480
- "create_acquisition_data",
481
- "load_image_data",
482
- "load_acquisition_data",
481
+ "detect_image_backend",
482
+ "get_backend",
483
483
  "is_sirf_backend",
484
484
  "is_stir_backend",
485
+ "load_acquisition_data",
486
+ "load_image_data",
487
+ "reset_backend",
488
+ "set_backend",
485
489
  "unwrap",
486
- "ImageDataInterface",
487
- "AcquisitionDataInterface",
488
490
  ]
@@ -6,7 +6,6 @@ for working with both SIRF and STIR Python libraries.
6
6
  """
7
7
 
8
8
  from abc import ABC, abstractmethod
9
- from typing import Tuple, Union
10
9
 
11
10
  import numpy as np
12
11
 
@@ -28,7 +27,6 @@ class ImageDataInterface(ABC):
28
27
  Returns:
29
28
  ImageDataInterface: Wrapped image object
30
29
  """
31
- pass
32
30
 
33
31
  @abstractmethod
34
32
  def write(self, filepath: str) -> None:
@@ -37,16 +35,14 @@ class ImageDataInterface(ABC):
37
35
  Args:
38
36
  filepath: Output file path
39
37
  """
40
- pass
41
38
 
42
39
  @abstractmethod
43
- def fill(self, data: Union[np.ndarray, float]) -> None:
40
+ def fill(self, data: np.ndarray | float) -> None:
44
41
  """Fill image with data.
45
42
 
46
43
  Args:
47
44
  data: Numpy array or scalar value
48
45
  """
49
- pass
50
46
 
51
47
  @abstractmethod
52
48
  def clone(self) -> "ImageDataInterface":
@@ -55,7 +51,6 @@ class ImageDataInterface(ABC):
55
51
  Returns:
56
52
  ImageDataInterface: Cloned image object
57
53
  """
58
- pass
59
54
 
60
55
  @abstractmethod
61
56
  def get_uniform_copy(self, value: float) -> "ImageDataInterface":
@@ -67,25 +62,22 @@ class ImageDataInterface(ABC):
67
62
  Returns:
68
63
  ImageDataInterface: New uniform image
69
64
  """
70
- pass
71
65
 
72
66
  @abstractmethod
73
- def dimensions(self) -> Tuple[int, int, int]:
67
+ def dimensions(self) -> tuple[int, int, int]:
74
68
  """Get image dimensions.
75
69
 
76
70
  Returns:
77
71
  Tuple[int, int, int]: (z, y, x) dimensions
78
72
  """
79
- pass
80
73
 
81
74
  @abstractmethod
82
- def voxel_sizes(self) -> Tuple[float, float, float]:
75
+ def voxel_sizes(self) -> tuple[float, float, float]:
83
76
  """Get voxel sizes in mm.
84
77
 
85
78
  Returns:
86
79
  Tuple[float, float, float]: (z, y, x) voxel sizes
87
80
  """
88
- pass
89
81
 
90
82
  @abstractmethod
91
83
  def as_array(self) -> np.ndarray:
@@ -94,7 +86,6 @@ class ImageDataInterface(ABC):
94
86
  Returns:
95
87
  np.ndarray: Image data as numpy array
96
88
  """
97
- pass
98
89
 
99
90
  @abstractmethod
100
91
  def sum(self) -> float:
@@ -103,7 +94,6 @@ class ImageDataInterface(ABC):
103
94
  Returns:
104
95
  float: Sum of all voxel values
105
96
  """
106
- pass
107
97
 
108
98
  @abstractmethod
109
99
  def max(self) -> float:
@@ -112,7 +102,6 @@ class ImageDataInterface(ABC):
112
102
  Returns:
113
103
  float: Maximum voxel value
114
104
  """
115
- pass
116
105
 
117
106
  @abstractmethod
118
107
  def maximum(self, value: float) -> None:
@@ -121,7 +110,6 @@ class ImageDataInterface(ABC):
121
110
  Args:
122
111
  value: Minimum value to clip to
123
112
  """
124
- pass
125
113
 
126
114
  @property
127
115
  @abstractmethod
@@ -131,53 +119,43 @@ class ImageDataInterface(ABC):
131
119
  Returns either SIRF ``ImageData`` or STIR
132
120
  ``FloatVoxelsOnCartesianGrid``.
133
121
  """
134
- pass
135
122
 
136
123
  # Arithmetic operations
137
124
  @abstractmethod
138
125
  def __add__(self, other):
139
126
  """Add two images or image and scalar."""
140
- pass
141
127
 
142
128
  @abstractmethod
143
129
  def __sub__(self, other):
144
130
  """Subtract two images or image and scalar."""
145
- pass
146
131
 
147
132
  @abstractmethod
148
133
  def __mul__(self, other):
149
134
  """Multiply image by scalar or element-wise."""
150
- pass
151
135
 
152
136
  @abstractmethod
153
137
  def __truediv__(self, other):
154
138
  """Divide image by scalar or element-wise."""
155
- pass
156
139
 
157
140
  @abstractmethod
158
141
  def __radd__(self, other):
159
142
  """Right-hand addition."""
160
- pass
161
143
 
162
144
  @abstractmethod
163
145
  def __rsub__(self, other):
164
146
  """Right-hand subtraction."""
165
- pass
166
147
 
167
148
  @abstractmethod
168
149
  def __rmul__(self, other):
169
150
  """Right-hand multiplication."""
170
- pass
171
151
 
172
152
  @abstractmethod
173
153
  def __rtruediv__(self, other):
174
154
  """Right-hand division."""
175
- pass
176
155
 
177
156
  @abstractmethod
178
157
  def __neg__(self):
179
158
  """Negate image."""
180
- pass
181
159
 
182
160
  @abstractmethod
183
161
  def sapyb(
@@ -195,7 +173,6 @@ class ImageDataInterface(ABC):
195
173
  Returns:
196
174
  ImageDataInterface: self after modification (for chaining)
197
175
  """
198
- pass
199
176
 
200
177
 
201
178
  class AcquisitionDataInterface(ABC):
@@ -212,7 +189,6 @@ class AcquisitionDataInterface(ABC):
212
189
  Returns:
213
190
  AcquisitionDataInterface: Wrapped acquisition object
214
191
  """
215
- pass
216
192
 
217
193
  @abstractmethod
218
194
  def write(self, filepath: str) -> None:
@@ -221,16 +197,14 @@ class AcquisitionDataInterface(ABC):
221
197
  Args:
222
198
  filepath: Output file path
223
199
  """
224
- pass
225
200
 
226
201
  @abstractmethod
227
- def fill(self, data: Union[np.ndarray, float]) -> None:
202
+ def fill(self, data: np.ndarray | float) -> None:
228
203
  """Fill acquisition data.
229
204
 
230
205
  Args:
231
206
  data: Numpy array or scalar value
232
207
  """
233
- pass
234
208
 
235
209
  @abstractmethod
236
210
  def clone(self) -> "AcquisitionDataInterface":
@@ -239,7 +213,6 @@ class AcquisitionDataInterface(ABC):
239
213
  Returns:
240
214
  AcquisitionDataInterface: Cloned object
241
215
  """
242
- pass
243
216
 
244
217
  @abstractmethod
245
218
  def get_uniform_copy(self, value: float) -> "AcquisitionDataInterface":
@@ -251,19 +224,17 @@ class AcquisitionDataInterface(ABC):
251
224
  Returns:
252
225
  AcquisitionDataInterface: New uniform acquisition data
253
226
  """
254
- pass
255
227
 
256
228
  @abstractmethod
257
- def dimensions(self) -> Tuple:
229
+ def dimensions(self) -> tuple:
258
230
  """Get acquisition data dimensions.
259
231
 
260
232
  Returns:
261
233
  Tuple: Dimensions (varies by format)
262
234
  """
263
- pass
264
235
 
265
236
  @property
266
- def shape(self) -> Tuple:
237
+ def shape(self) -> tuple:
267
238
  """Get acquisition data shape (alias for dimensions()).
268
239
 
269
240
  Returns:
@@ -278,7 +249,6 @@ class AcquisitionDataInterface(ABC):
278
249
  Returns:
279
250
  np.ndarray: Acquisition data as numpy array
280
251
  """
281
- pass
282
252
 
283
253
  @abstractmethod
284
254
  def sum(self) -> float:
@@ -287,7 +257,6 @@ class AcquisitionDataInterface(ABC):
287
257
  Returns:
288
258
  float: Sum of all counts
289
259
  """
290
- pass
291
260
 
292
261
  @abstractmethod
293
262
  def max(self) -> float:
@@ -296,7 +265,6 @@ class AcquisitionDataInterface(ABC):
296
265
  Returns:
297
266
  float: Maximum bin value
298
267
  """
299
- pass
300
268
 
301
269
  @abstractmethod
302
270
  def get_info(self) -> str:
@@ -305,68 +273,56 @@ class AcquisitionDataInterface(ABC):
305
273
  Returns:
306
274
  str: Metadata string
307
275
  """
308
- pass
309
276
 
310
277
  @abstractmethod
311
- def get_energy_window_bounds(self) -> Tuple[float, float]:
278
+ def get_energy_window_bounds(self) -> tuple[float, float]:
312
279
  """Get the energy window bounds for the acquisition.
313
280
 
314
281
  Returns:
315
282
  Tuple[float, float]: (lower_threshold, upper_threshold) in keV
316
283
  """
317
- pass
318
284
 
319
285
  @property
320
286
  @abstractmethod
321
287
  def native_object(self):
322
288
  """Get the underlying native object (AcquisitionData or ProjData)."""
323
- pass
324
289
 
325
290
  # Arithmetic operations
326
291
  @abstractmethod
327
292
  def __add__(self, other):
328
293
  """Add two acquisition data or acquisition data and scalar."""
329
- pass
330
294
 
331
295
  @abstractmethod
332
296
  def __sub__(self, other):
333
297
  """Subtract two acquisition data or acquisition data and scalar."""
334
- pass
335
298
 
336
299
  @abstractmethod
337
300
  def __mul__(self, other):
338
301
  """Multiply acquisition data by scalar or element-wise."""
339
- pass
340
302
 
341
303
  @abstractmethod
342
304
  def __truediv__(self, other):
343
305
  """Divide acquisition data by scalar or element-wise."""
344
- pass
345
306
 
346
307
  @abstractmethod
347
308
  def __radd__(self, other):
348
309
  """Right-hand addition."""
349
- pass
350
310
 
351
311
  @abstractmethod
352
312
  def __rsub__(self, other):
353
313
  """Right-hand subtraction."""
354
- pass
355
314
 
356
315
  @abstractmethod
357
316
  def __rmul__(self, other):
358
317
  """Right-hand multiplication."""
359
- pass
360
318
 
361
319
  @abstractmethod
362
320
  def __rtruediv__(self, other):
363
321
  """Right-hand division."""
364
- pass
365
322
 
366
323
  @abstractmethod
367
324
  def __neg__(self):
368
325
  """Negate acquisition data."""
369
- pass
370
326
 
371
327
  @abstractmethod
372
328
  def sapyb(
@@ -384,4 +340,3 @@ class AcquisitionDataInterface(ABC):
384
340
  Returns:
385
341
  AcquisitionDataInterface: self after modification (for chaining)
386
342
  """
387
- pass
@@ -5,7 +5,7 @@ Provides wrappers around sirf.STIR.ImageData and sirf.STIR.AcquisitionData
5
5
  that conform to the backend interface.
6
6
  """
7
7
 
8
- from typing import Tuple, Union
8
+ from typing import Union
9
9
 
10
10
  import numpy as np
11
11
 
@@ -60,21 +60,17 @@ class SirfImageData(ImageDataInterface):
60
60
  """Create uniform copy."""
61
61
  return SirfImageData(self._obj.get_uniform_copy(value))
62
62
 
63
- def dimensions(self) -> Tuple[int, int, int]:
63
+ def dimensions(self) -> tuple[int, int, int]:
64
64
  """Get dimensions as (z, y, x)."""
65
65
  return self._obj.dimensions()
66
66
 
67
- def voxel_sizes(self) -> Tuple[float, float, float]:
67
+ def voxel_sizes(self) -> tuple[float, float, float]:
68
68
  """Get voxel sizes as (z, y, x) in mm."""
69
69
  return self._obj.voxel_sizes()
70
70
 
71
71
  def as_array(self) -> np.ndarray:
72
72
  """Convert to numpy array."""
73
- # Try asarray first (newer SIRF), fall back to as_array
74
- if hasattr(self._obj, "asarray"):
75
- return self._obj.asarray()
76
- else:
77
- return self._obj.as_array()
73
+ return self._obj.as_array()
78
74
 
79
75
  def sum(self) -> float:
80
76
  """Sum all values."""
@@ -124,16 +120,18 @@ class SirfImageData(ImageDataInterface):
124
120
  return SirfImageData(self._obj / other)
125
121
 
126
122
  def __radd__(self, other):
127
- return SirfImageData(other + self._obj)
123
+ return self + other
128
124
 
129
125
  def __rsub__(self, other):
130
- return SirfImageData(other - self._obj)
126
+ return -self + other
131
127
 
132
128
  def __rmul__(self, other):
133
- return SirfImageData(other * self._obj)
129
+ return self * other
134
130
 
135
131
  def __rtruediv__(self, other):
136
- return SirfImageData(other / self._obj)
132
+ if isinstance(other, ImageData):
133
+ return SirfImageData(other) / self
134
+ return self.get_uniform_copy(float(other)) / self
137
135
 
138
136
  def __neg__(self):
139
137
  return SirfImageData(-self._obj)
@@ -197,17 +195,13 @@ class SirfAcquisitionData(AcquisitionDataInterface):
197
195
  """Create uniform copy."""
198
196
  return SirfAcquisitionData(self._obj.get_uniform_copy(value))
199
197
 
200
- def dimensions(self) -> Tuple:
198
+ def dimensions(self) -> tuple:
201
199
  """Get dimensions."""
202
200
  return self._obj.dimensions()
203
201
 
204
202
  def as_array(self) -> np.ndarray:
205
203
  """Convert to numpy array."""
206
- # Try asarray first (newer SIRF), fall back to as_array
207
- if hasattr(self._obj, "asarray"):
208
- return self._obj.asarray()
209
- else:
210
- return self._obj.as_array()
204
+ return self._obj.as_array()
211
205
 
212
206
  def sum(self) -> float:
213
207
  """Sum all values."""
@@ -223,7 +217,7 @@ class SirfAcquisitionData(AcquisitionDataInterface):
223
217
  """Get metadata info."""
224
218
  return self._obj.get_info()
225
219
 
226
- def get_energy_window_bounds(self) -> Tuple[float, float]:
220
+ def get_energy_window_bounds(self) -> tuple[float, float]:
227
221
  """Get the energy window bounds for the acquisition.
228
222
 
229
223
  Returns:
@@ -277,16 +271,18 @@ class SirfAcquisitionData(AcquisitionDataInterface):
277
271
  return SirfAcquisitionData(self._obj / other)
278
272
 
279
273
  def __radd__(self, other):
280
- return SirfAcquisitionData(other + self._obj)
274
+ return self + other
281
275
 
282
276
  def __rsub__(self, other):
283
- return SirfAcquisitionData(other - self._obj)
277
+ return -self + other
284
278
 
285
279
  def __rmul__(self, other):
286
- return SirfAcquisitionData(other * self._obj)
280
+ return self * other
287
281
 
288
282
  def __rtruediv__(self, other):
289
- return SirfAcquisitionData(other / self._obj)
283
+ if isinstance(other, AcquisitionData):
284
+ return SirfAcquisitionData(other) / self
285
+ return self.get_uniform_copy(float(other)) / self
290
286
 
291
287
  def __neg__(self):
292
288
  return SirfAcquisitionData(-self._obj)