simind-python-connector 1.0.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 (47) hide show
  1. simind_python_connector/__init__.py +72 -0
  2. simind_python_connector/backends/__init__.py +480 -0
  3. simind_python_connector/backends/base.py +387 -0
  4. simind_python_connector/backends/sirf_backend.py +309 -0
  5. simind_python_connector/backends/stir_backend.py +395 -0
  6. simind_python_connector/builders/__init__.py +19 -0
  7. simind_python_connector/builders/acquisition_builder.py +526 -0
  8. simind_python_connector/builders/image_builder.py +217 -0
  9. simind_python_connector/configs/AnyScan.yaml +420 -0
  10. simind_python_connector/configs/Discovery670.yaml +412 -0
  11. simind_python_connector/configs/Example.yaml +420 -0
  12. simind_python_connector/configs/MLD001_SCAN0.yaml +426 -0
  13. simind_python_connector/configs/__init__.py +41 -0
  14. simind_python_connector/configs/input.smc +51 -0
  15. simind_python_connector/connectors/__init__.py +24 -0
  16. simind_python_connector/connectors/_spacing.py +69 -0
  17. simind_python_connector/connectors/base.py +40 -0
  18. simind_python_connector/connectors/python_connector.py +355 -0
  19. simind_python_connector/connectors/pytomography_adaptor.py +263 -0
  20. simind_python_connector/connectors/sirf_adaptor.py +164 -0
  21. simind_python_connector/connectors/stir_adaptor.py +164 -0
  22. simind_python_connector/converters/__init__.py +16 -0
  23. simind_python_connector/converters/attenuation.py +367 -0
  24. simind_python_connector/converters/dicom_to_stir.py +3 -0
  25. simind_python_connector/converters/simind_to_stir.py +769 -0
  26. simind_python_connector/core/__init__.py +7 -0
  27. simind_python_connector/core/config.py +939 -0
  28. simind_python_connector/core/executor.py +96 -0
  29. simind_python_connector/core/types.py +203 -0
  30. simind_python_connector/data/Schneider2000.json +222 -0
  31. simind_python_connector/data/__init__.py +25 -0
  32. simind_python_connector/data/bone.atn +187 -0
  33. simind_python_connector/data/h2o.atn +92 -0
  34. simind_python_connector/utils/__init__.py +120 -0
  35. simind_python_connector/utils/backend_access.py +121 -0
  36. simind_python_connector/utils/import_helpers.py +74 -0
  37. simind_python_connector/utils/interfile_numpy.py +195 -0
  38. simind_python_connector/utils/interfile_parser.py +175 -0
  39. simind_python_connector/utils/io_utils.py +14 -0
  40. simind_python_connector/utils/simind_utils.py +70 -0
  41. simind_python_connector/utils/sirf_stir_utils.py +194 -0
  42. simind_python_connector/utils/stir_utils.py +485 -0
  43. simind_python_connector-1.0.0.dist-info/METADATA +274 -0
  44. simind_python_connector-1.0.0.dist-info/RECORD +47 -0
  45. simind_python_connector-1.0.0.dist-info/WHEEL +5 -0
  46. simind_python_connector-1.0.0.dist-info/licenses/LICENSE +195 -0
  47. simind_python_connector-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,426 @@
1
+ metadata:
2
+ comment: MLD001_SCAN0
3
+ source_file: MLD001_SCAN0.smc
4
+ parameters:
5
+ source:
6
+ photon_energy:
7
+ index: 1
8
+ value: 208.0
9
+ description: Photon energy in keV (e.g., 140 for 99mTc, 208 for 177Lu)
10
+ source_half_length:
11
+ index: 2
12
+ value: 28.267
13
+ description: Source half-length in cm
14
+ source_half_width:
15
+ index: 3
16
+ value: 28.267
17
+ description: Source half-width in cm
18
+ source_half_height:
19
+ index: 4
20
+ value: 28.267
21
+ description: Source half-height in cm
22
+ source_type:
23
+ index: 15
24
+ value: -1.0
25
+ description: Source type code (0=sphere, 1=cylinder, etc.) - check SIMIND manual
26
+ shift_source_x:
27
+ index: 16
28
+ value: 0.0
29
+ description: Shift of source in x-direction (cm)
30
+ shift_source_y:
31
+ index: 17
32
+ value: 0.0
33
+ description: Shift of source in y-direction (cm)
34
+ shift_source_z:
35
+ index: 18
36
+ value: 0.0
37
+ description: Shift of source in z-direction (cm)
38
+ photon_direction:
39
+ index: 19
40
+ value: 2.0
41
+ description: Photon direction code (2=isotropic typical)
42
+ emitted_photons_per_decay:
43
+ index: 24
44
+ value: 0.879
45
+ description: Number of photons emitted per decay
46
+ source_activity:
47
+ index: 25
48
+ value: 21238.0
49
+ description: Source activity in MBq
50
+ number_photon_histories:
51
+ index: 26
52
+ value: 1.0
53
+ description: Number of photon histories to simulate (10^6 typical minimum)
54
+ matrix_size_source_map_i:
55
+ index: 79
56
+ value: 128.0
57
+ description: Matrix size for source map (i-direction) - 128x128 standard
58
+ matrix_size_source_map_j:
59
+ index: 82
60
+ value: 0.0
61
+ description: Matrix size for source map (j-direction) - 128x128 standard
62
+ phantom:
63
+ phantom_half_length:
64
+ index: 5
65
+ value: 28.267
66
+ description: Phantom half-length in cm
67
+ phantom_half_width:
68
+ index: 6
69
+ value: 28.267
70
+ description: Phantom half-width in cm
71
+ phantom_half_height:
72
+ index: 7
73
+ value: 28.267
74
+ description: Phantom half-height in cm
75
+ phantom_type:
76
+ index: 14
77
+ value: -1.0
78
+ description: Phantom type code
79
+ pixel_size_density_images:
80
+ index: 31
81
+ value: 0.44167
82
+ description: Pixel size for density images (cm)
83
+ orientation_density_images:
84
+ index: 32
85
+ value: 0.0
86
+ description: Orientation of density images
87
+ first_image_density_images:
88
+ index: 33
89
+ value: 1.0
90
+ description: First image number for density images
91
+ number_density_images:
92
+ index: 34
93
+ value: 128.0
94
+ description: Number of density images
95
+ density_limit_border:
96
+ index: 35
97
+ value: 0.01
98
+ description: Density limit at border
99
+ shift_density_images_y:
100
+ index: 36
101
+ value: 0.0
102
+ description: Shift of density images in y-direction (cm)
103
+ shift_density_images_z:
104
+ index: 37
105
+ value: 0.0
106
+ description: Shift of density images in z-direction (cm)
107
+ shift_density_images_x:
108
+ index: 39
109
+ value: 0.0
110
+ description: Shift of density images in x-direction (cm)
111
+ density_threshold_soft_bone:
112
+ index: 40
113
+ value: 0.0
114
+ description: Density threshold for soft bone
115
+ code_definitions_zubal_phantom:
116
+ index: 45
117
+ value: 1.0
118
+ description: Code definitions for Zubal phantom
119
+ matrix_size_density_map_i:
120
+ index: 78
121
+ value: 128.0
122
+ description: Matrix size for density map (i-direction)
123
+ matrix_size_density_map_j:
124
+ index: 81
125
+ value: 0.0
126
+ description: Matrix size for density map (j-direction)
127
+ detector_crystal:
128
+ crystal_half_length_radius:
129
+ index: 8
130
+ value: 20.0
131
+ description: Crystal half-length/radius in cm (circular detectors use radius)
132
+ crystal_thickness:
133
+ index: 9
134
+ value: 0.9525
135
+ description: 'Crystal thickness in cm (NaI(Tl) typical: 0.95cm)'
136
+ crystal_half_width:
137
+ index: 10
138
+ value: 27.0
139
+ description: Crystal half-width in cm (for rectangular crystals)
140
+ height_to_detector_surface:
141
+ index: 12
142
+ value: 20.0
143
+ description: Height from collimator to detector surface (cm)
144
+ cover_thickness:
145
+ index: 13
146
+ value: 0.1
147
+ description: Cover thickness in cm (typically Al or Be window)
148
+ energy_resolution:
149
+ index: 22
150
+ value: 12.0
151
+ description: Energy resolution FWHM (%) at reference energy (9-12% typical at
152
+ 140keV)
153
+ intrinsic_resolution:
154
+ index: 23
155
+ value: 0.55
156
+ description: Intrinsic spatial resolution FWHM (cm) (3-4mm typical)
157
+ voltage:
158
+ index: 91
159
+ value: 0.0
160
+ description: Applied voltage (V) for semiconductor detectors
161
+ mobility_life_electrons:
162
+ index: 92
163
+ value: 0.0
164
+ description: Mobility-life product for electrons (semiconductor detectors)
165
+ mobility_life_holes:
166
+ index: 93
167
+ value: 0.0
168
+ description: Mobility-life product for holes (semiconductor detectors)
169
+ contact_pad_size:
170
+ index: 94
171
+ value: 0.0
172
+ description: Contact pad size (cm) for pixelated detectors
173
+ anode_element_pitch:
174
+ index: 95
175
+ value: 0.0
176
+ description: Anode element pitch (cm) for pixelated detectors
177
+ exponential_decay_constant_tau:
178
+ index: 96
179
+ value: 0.0
180
+ description: Exponential decay constant tau for charge collection
181
+ components_hecht_formula:
182
+ index: 97
183
+ value: 0.0
184
+ description: Components for Hecht formula (charge collection efficiency)
185
+ energy_resolution_model:
186
+ index: 98
187
+ value: 0.0
188
+ description: Energy resolution model code (check SIMIND manual for options)
189
+ cloud_mobility:
190
+ index: 99
191
+ value: 0.0
192
+ description: Cloud mobility parameter for charge collection
193
+ detector_array_size_i:
194
+ index: 100
195
+ value: 0.0
196
+ description: Detector array size (i-direction) for pixelated systems
197
+ detector_array_size_j:
198
+ index: 101
199
+ value: 0.0
200
+ description: Detector array size (j-direction) for pixelated systems
201
+ collimator:
202
+ hole_size_x:
203
+ index: 46
204
+ value: 0.25
205
+ description: 'Collimator hole diameter (cm) - LEHR: 0.111cm, HEGP: 0.24cm'
206
+ hole_size_y:
207
+ index: 47
208
+ value: 0.28868
209
+ description: Collimator hole diameter (cm) - should match hole_size_x for round
210
+ holes
211
+ distance_between_holes_x:
212
+ index: 48
213
+ value: 0.03
214
+ description: 'Distance between hole centers (cm) - LEHR: 0.16cm septal thickness'
215
+ distance_between_holes_y:
216
+ index: 49
217
+ value: 0.1963
218
+ description: Distance between hole centers (cm) - hexagonal pattern spacing
219
+ shift_center_hole_x:
220
+ index: 50
221
+ value: 0.14
222
+ description: Shift of center hole in x-direction (cm) for alignment
223
+ shift_center_hole_y:
224
+ index: 51
225
+ value: 0.24249
226
+ description: Shift of center hole in y-direction (cm) for alignment
227
+ collimator_thickness:
228
+ index: 52
229
+ value: 4.1
230
+ description: 'Collimator thickness (cm) - LEHR: 2.405cm, HEGP: 5.9cm'
231
+ collimator_routine:
232
+ index: 53
233
+ value: 0.0
234
+ description: Collimator routine code (0=no collimator, 1=parallel holes, etc.)
235
+ hole_shape:
236
+ index: 54
237
+ value: 3.0
238
+ description: Hole shape code (0=round, 1=square, 2=hexagonal)
239
+ distance_collimator_detector:
240
+ index: 56
241
+ value: 0.0
242
+ description: Distance from collimator face to detector surface (cm)
243
+ random_collimator_movement:
244
+ index: 59
245
+ value: 0.0
246
+ description: Random collimator movement parameter (for manufacturing variations)
247
+ energy_analysis:
248
+ upper_window_threshold:
249
+ index: 20
250
+ value: -100.0
251
+ description: Upper energy window threshold (keV) - set to -100 for automatic
252
+ lower_window_threshold:
253
+ index: 21
254
+ value: -100.0
255
+ description: Lower energy window threshold (keV) - set to -100 for automatic
256
+ kev_per_channel:
257
+ index: 27
258
+ value: 0.5
259
+ description: keV per channel for energy spectrum binning
260
+ energy_spectra_channels:
261
+ index: 80
262
+ value: 512.0
263
+ description: Number of energy spectra channels (512 typical)
264
+ cutoff_energy_terminate_photon_history:
265
+ index: 83
266
+ value: 0.0
267
+ description: Cutoff energy to terminate photon history (keV)
268
+ spect_imaging:
269
+ pixel_size_simulated_image:
270
+ index: 28
271
+ value: 0.44167
272
+ description: Pixel size for simulated images (cm) - affects resolution vs FOV
273
+ spect_no_projections:
274
+ index: 29
275
+ value: 120.0
276
+ description: Number of SPECT projections (64, 120, 128 typical)
277
+ spect_rotation:
278
+ index: 30
279
+ value: 2.0
280
+ description: "SPECT rotation parameter (2=360\xB0 typical)"
281
+ spect_starting_angle:
282
+ index: 41
283
+ value: 0.0
284
+ description: "SPECT starting angle (degrees) - 0\xB0 = anterior"
285
+ spect_orbital_rotation_fraction:
286
+ index: 42
287
+ value: -1.0
288
+ description: SPECT orbital rotation fraction (1.0 = full orbit)
289
+ camera_offset_x:
290
+ index: 43
291
+ value: 0.0
292
+ description: Camera offset in x-direction (cm) from rotation center
293
+ camera_offset_y:
294
+ index: 44
295
+ value: 0.0
296
+ description: Camera offset in y-direction (cm) from rotation center
297
+ matrix_size_image_i:
298
+ index: 76
299
+ value: 128.0
300
+ description: Matrix size for images (i-direction) - 128x128 standard
301
+ matrix_size_image_j:
302
+ index: 77
303
+ value: 128.0
304
+ description: Matrix size for images (j-direction) - 128x128 standard
305
+ simulation_control:
306
+ step_size_photon_path_simulation:
307
+ index: 38
308
+ value: 0.44167
309
+ description: Step size for photon path simulation (cm) - smaller = more accurate
310
+ type:
311
+ index: 55
312
+ value: 0.0
313
+ description: General type parameter - check SIMIND manual for current meaning
314
+ scoring_routine:
315
+ index: 84
316
+ value: 1.0
317
+ description: Scoring routine code - affects output data collection
318
+ csv_file_content:
319
+ index: 85
320
+ value: 0.0
321
+ description: CSV file content parameter - for custom data output
322
+ unused_parameters:
323
+ unused_parameter_1:
324
+ index: 57
325
+ value: 0.0
326
+ description: Unused parameter 1 - reserved for future use
327
+ unused_parameter_2:
328
+ index: 58
329
+ value: 0.0
330
+ description: Unused parameter 2 - reserved for future use
331
+ unused_parameter_3:
332
+ index: 60
333
+ value: 0.0
334
+ description: Unused parameter 3 - reserved for future use
335
+ simulation_flags:
336
+ write_results_to_screen:
337
+ index: 1
338
+ enabled: true
339
+ write_images_to_files:
340
+ index: 2
341
+ enabled: false
342
+ write_pulse_height_distribution_to_file:
343
+ index: 3
344
+ enabled: true
345
+ include_collimator:
346
+ index: 4
347
+ enabled: true
348
+ simulate_spect_study:
349
+ index: 5
350
+ enabled: true
351
+ include_characteristic_xray_emissions:
352
+ index: 6
353
+ enabled: true
354
+ include_backscattering_material:
355
+ index: 7
356
+ enabled: true
357
+ use_random_seed_value:
358
+ index: 8
359
+ enabled: true
360
+ currently_not_in_use:
361
+ index: 9
362
+ enabled: false
363
+ include_interactions_in_cover:
364
+ index: 10
365
+ enabled: true
366
+ include_interactions_in_phantom:
367
+ index: 11
368
+ enabled: true
369
+ include_energy_resolution_in_crystal:
370
+ index: 12
371
+ enabled: true
372
+ include_forced_interactions_in_crystal:
373
+ index: 13
374
+ enabled: false
375
+ write_interfile_header_files:
376
+ index: 14
377
+ enabled: true
378
+ save_aligned_phantom_images:
379
+ index: 15
380
+ enabled: false
381
+ text_variables:
382
+ 1: ge-legp
383
+ 2: none
384
+ 3: none
385
+ 4: none
386
+ 5: none
387
+ 6: none
388
+ 7: none
389
+ 8: none
390
+ data_files:
391
+ phantom_soft_tissue:
392
+ index: 1
393
+ filepath: h2o
394
+ phantom_bone:
395
+ index: 2
396
+ filepath: bone
397
+ cover_material:
398
+ index: 3
399
+ filepath: al
400
+ crystal_material:
401
+ index: 4
402
+ filepath: nai
403
+ image_file_phantom:
404
+ index: 5
405
+ filepath: tmp_density
406
+ image_file_source:
407
+ index: 6
408
+ filepath: tmp_source
409
+ backscatter_material:
410
+ index: 7
411
+ filepath: pmt
412
+ energy_resolution_file:
413
+ index: 8
414
+ filepath: none
415
+ unknown_file_1:
416
+ index: 9
417
+ filepath: none
418
+ unknown_file_2:
419
+ index: 10
420
+ filepath: none
421
+ unknown_file_3:
422
+ index: 11
423
+ filepath: none
424
+ unknown_file_4:
425
+ index: 12
426
+ filepath: none
@@ -0,0 +1,41 @@
1
+ """
2
+ Packaged scanner presets (YAML or SMC).
3
+
4
+ Example
5
+ -------
6
+ >>> from simind_python_connector.configs import get
7
+ >>> cfg = get("AnyScan.yaml") # Path object
8
+
9
+ >>> from simind_python_connector.core import SimulationConfig
10
+ >>> sim_cfg = SimulationConfig(cfg) # load file
11
+ """
12
+
13
+ from importlib import resources as _res
14
+ from pathlib import Path
15
+ from typing import Union
16
+
17
+
18
+ __all__ = ["get", "list"]
19
+
20
+
21
+ def get(name: str) -> Path:
22
+ """
23
+ Return a **Path** to a bundled config file.
24
+
25
+ Parameters
26
+ ----------
27
+ name : str
28
+ Filename, e.g. ``"AnyScan.yaml"`` or ``"input.smc"``.
29
+ """
30
+ return _res.files(__package__).joinpath(name)
31
+
32
+
33
+ def list(ext: Union[str, tuple[str, ...]] = (".yaml", ".smc")) -> list[str]:
34
+ """
35
+ List files in this package (default: *.yaml / *.smc).
36
+ """
37
+ return [
38
+ p.name
39
+ for p in _res.files(__package__).iterdir()
40
+ if p.suffix in (ext if isinstance(ext, tuple) else (ext,))
41
+ ]
@@ -0,0 +1,51 @@
1
+ SMCV2
2
+ Test simulation as a benchmark
3
+ 120 # Basic Change data
4
+ 0.14000E+03 0.10000E+00 0.10000E+00 0.10000E+00 0.83200E+01
5
+ 0.11000E+02 0.11000E+02 0.25000E+02 0.95300E+00 0.00000E+00
6
+ 0.10000E+02 0.25000E+02 0.10000E+00 0.40000E+01 0.50000E+01
7
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.20000E+01-0.20000E+02
8
+ -0.20000E+02 0.88000E+01 0.34000E+00 0.87900E+00 0.10000E+01
9
+ 0.10000E+02 0.50000E+00 0.44000E+00 0.64000E+02 0.00000E+00
10
+ 0.10000E+00 0.00000E+00 0.10000E+01 0.18600E+03 0.10000E-01
11
+ 0.00000E+00 0.00000E+00 0.10000E+00 0.00000E+00 0.00000E+00
12
+ 0.00000E+00 0.10000E+01 0.00000E+00 0.00000E+00 0.10000E+01
13
+ 0.25000E+00 0.28868E+00 0.30000E-01 0.19630E+00 0.14000E+00
14
+ 0.24249E+00 0.41000E+01 0.00000E+00 0.30000E+01 0.00000E+00
15
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
16
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
17
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
18
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
19
+ 0.64000E+02 0.64000E+02 0.25600E+03 0.25600E+03 0.51200E+03
20
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
21
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
22
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
23
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
24
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
25
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
26
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
27
+ 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
28
+ 30 # Simulation flags
29
+ TFTTFFTFFFTTFFFFFFFFFFFFFFFFFF
30
+ 8 # Text Variables
31
+ ge-legp
32
+ none
33
+ none
34
+ none
35
+ none
36
+ none
37
+ none
38
+ none
39
+ 12 # Data files
40
+ h2o
41
+ h2o
42
+ al
43
+ nai
44
+ cyl_un1
45
+ vox_brn
46
+ pmt
47
+ none
48
+ none
49
+ none
50
+ none
51
+ none
@@ -0,0 +1,24 @@
1
+ """Connector/adaptor APIs."""
2
+
3
+ from .base import BaseConnector
4
+ from .python_connector import (
5
+ NumpyConnector,
6
+ ProjectionResult,
7
+ RuntimeOperator,
8
+ SimindPythonConnector,
9
+ )
10
+ from .pytomography_adaptor import PyTomographySimindAdaptor
11
+ from .sirf_adaptor import SirfSimindAdaptor
12
+ from .stir_adaptor import StirSimindAdaptor
13
+
14
+
15
+ __all__ = [
16
+ "BaseConnector",
17
+ "NumpyConnector",
18
+ "PyTomographySimindAdaptor",
19
+ "ProjectionResult",
20
+ "RuntimeOperator",
21
+ "SirfSimindAdaptor",
22
+ "SimindPythonConnector",
23
+ "StirSimindAdaptor",
24
+ ]
@@ -0,0 +1,69 @@
1
+ """Helpers for extracting voxel spacing from SIRF/STIR image objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ def _call_or_value(value: Any) -> Any:
9
+ return value() if callable(value) else value
10
+
11
+
12
+ def _voxel_size_from_spacing(spacing: Any) -> float | None:
13
+ try:
14
+ values = tuple(float(v) for v in spacing)
15
+ except Exception:
16
+ values = ()
17
+
18
+ if values:
19
+ if len(values) >= 4:
20
+ return float(values[3])
21
+ if len(values) >= 3:
22
+ return float(values[2])
23
+
24
+ if hasattr(spacing, "z"):
25
+ try:
26
+ return float(_call_or_value(getattr(spacing, "z")))
27
+ except Exception:
28
+ pass
29
+
30
+ if hasattr(spacing, "at"):
31
+ for index in (3, 2):
32
+ try:
33
+ return float(spacing.at(index))
34
+ except Exception:
35
+ continue
36
+
37
+ for index in (3, 2):
38
+ try:
39
+ return float(spacing[index])
40
+ except Exception:
41
+ continue
42
+
43
+ return None
44
+
45
+
46
+ def extract_voxel_size_mm(image: Any, backend_name: str) -> float:
47
+ """Extract z voxel spacing in mm from backend image metadata."""
48
+ if hasattr(image, "voxel_sizes"):
49
+ voxel_sizes = image.voxel_sizes()
50
+ try:
51
+ if len(voxel_sizes) >= 3:
52
+ return float(voxel_sizes[2])
53
+ except Exception:
54
+ pass
55
+
56
+ if hasattr(image, "get_grid_spacing"):
57
+ voxel_size_mm = _voxel_size_from_spacing(image.get_grid_spacing())
58
+ if voxel_size_mm is not None:
59
+ return voxel_size_mm
60
+ raise ValueError(
61
+ f"Unable to read voxel spacing from {backend_name} get_grid_spacing()."
62
+ )
63
+
64
+ raise ValueError(
65
+ f"{backend_name} source object must expose voxel_sizes() or get_grid_spacing()."
66
+ )
67
+
68
+
69
+ __all__ = ["extract_voxel_size_mm"]
@@ -0,0 +1,40 @@
1
+ """Abstract connector contract shared by all connector/adaptor implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any, Dict, Mapping
7
+
8
+ from simind_python_connector.core.config import SimulationConfig
9
+
10
+
11
+ class BaseConnector(ABC):
12
+ """Minimal interface for all SIMIND connector styles."""
13
+
14
+ @abstractmethod
15
+ def add_config_value(self, index: int, value: Any) -> None:
16
+ """Set a SIMIND config value by index."""
17
+
18
+ @abstractmethod
19
+ def add_runtime_switch(self, switch: str, value: Any) -> None:
20
+ """Set one runtime switch."""
21
+
22
+ def set_runtime_switches(self, switches: Mapping[str, Any]) -> None:
23
+ """Set multiple runtime switches."""
24
+ for switch, value in switches.items():
25
+ self.add_runtime_switch(switch, value)
26
+
27
+ @abstractmethod
28
+ def run(self, *args: Any, **kwargs: Any) -> Any:
29
+ """Execute simulation and return connector-native outputs."""
30
+
31
+ @abstractmethod
32
+ def get_outputs(self) -> Dict[str, Any]:
33
+ """Return outputs from the latest successful run."""
34
+
35
+ @abstractmethod
36
+ def get_config(self) -> SimulationConfig:
37
+ """Return active simulation configuration."""
38
+
39
+
40
+ __all__ = ["BaseConnector"]