simind-python-connector 1.1.2__tar.gz → 1.2.0__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 (154) hide show
  1. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/MANIFEST.in +0 -4
  2. {simind_python_connector-1.1.2/simind_python_connector.egg-info → simind_python_connector-1.2.0}/PKG-INFO +4 -1
  3. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/README.md +3 -0
  4. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/README.md +3 -0
  5. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/compose.yaml +4 -0
  6. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/constraints.txt +2 -0
  7. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/python/Dockerfile +1 -1
  8. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/pytomography/Dockerfile +2 -3
  9. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/sirf/Dockerfile +2 -1
  10. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/stir/Dockerfile +2 -1
  11. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/changelog.rst +58 -0
  12. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/conf.py +5 -8
  13. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/contributing.rst +21 -0
  14. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/01_basic_simulation.py +2 -1
  15. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/02_runtime_switch_comparison.py +1 -0
  16. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/03_multi_window.py +1 -0
  17. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/05_scattwin_vs_penetrate_comparison.py +6 -2
  18. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/07A_stir_adaptor_osem.py +1 -0
  19. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0/docs}/examples/07B_sirf_adaptor_osem.py +1 -0
  20. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/07C_pytomography_adaptor_osem.py +1 -0
  21. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/installation.rst +10 -0
  22. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/testing.rst +21 -54
  23. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/usage.rst +31 -12
  24. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/01_basic_simulation.py +2 -1
  25. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/02_runtime_switch_comparison.py +1 -0
  26. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/03_multi_window.py +1 -0
  27. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/05_scattwin_vs_penetrate_comparison.py +6 -2
  28. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/07A_stir_adaptor_osem.py +1 -0
  29. {simind_python_connector-1.1.2/docs → simind_python_connector-1.2.0}/examples/07B_sirf_adaptor_osem.py +1 -0
  30. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/07C_pytomography_adaptor_osem.py +1 -0
  31. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/pyproject.toml +6 -4
  32. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/scripts/release.sh +28 -20
  33. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/scripts/run_container_examples.sh +2 -0
  34. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/scripts/run_container_validation.sh +4 -0
  35. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/__init__.py +2 -11
  36. simind_python_connector-1.2.0/simind_python_connector/_version.py +3 -0
  37. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/backends/__init__.py +30 -28
  38. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/backends/base.py +7 -52
  39. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/backends/sirf_backend.py +19 -23
  40. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/backends/stir_backend.py +38 -29
  41. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/builders/__init__.py +2 -2
  42. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/builders/acquisition_builder.py +8 -25
  43. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/builders/image_builder.py +4 -4
  44. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/configs/__init__.py +1 -2
  45. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/connectors/_spacing.py +4 -16
  46. simind_python_connector-1.2.0/simind_python_connector/connectors/base.py +73 -0
  47. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/connectors/python_connector.py +40 -25
  48. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/connectors/pytomography_adaptor.py +28 -37
  49. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/connectors/sirf_adaptor.py +10 -24
  50. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/connectors/stir_adaptor.py +13 -24
  51. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/converters/attenuation.py +1 -10
  52. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/converters/simind_to_stir.py +32 -32
  53. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/core/config.py +1 -0
  54. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/core/executor.py +9 -10
  55. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/__init__.py +7 -17
  56. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/backend_access.py +4 -4
  57. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/import_helpers.py +4 -5
  58. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/interfile_numpy.py +11 -4
  59. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/interfile_parser.py +9 -10
  60. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/simind_utils.py +0 -7
  61. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/sirf_stir_utils.py +11 -11
  62. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/stir_utils.py +12 -18
  63. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0/simind_python_connector.egg-info}/PKG-INFO +4 -1
  64. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector.egg-info/SOURCES.txt +2 -0
  65. simind_python_connector-1.2.0/tests/conftest.py +47 -0
  66. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_acquisition_builder_unit.py +0 -11
  67. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_adaptor_release_simind.py +44 -35
  68. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_anisotropic_voxels_simind.py +1 -1
  69. simind_python_connector-1.2.0/tests/test_arithmetic_operations.py +84 -0
  70. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_attenuation_utils.py +5 -2
  71. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_backends.py +18 -183
  72. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_connector_backend_separation.py +12 -2
  73. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_container_scripts.py +6 -12
  74. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_dicom_builder.py +18 -4
  75. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_energy_window_filename_simind.py +1 -2
  76. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_executor.py +0 -9
  77. simind_python_connector-1.2.0/tests/test_integration.py +33 -0
  78. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_interfile_numpy.py +3 -2
  79. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_native_adaptors.py +99 -105
  80. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_native_geometry_real.py +4 -2
  81. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_non_square_slices_simind.py +2 -1
  82. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_packaging_resources.py +12 -3
  83. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_python_connector.py +31 -2
  84. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_pytomography_adaptor.py +23 -21
  85. simind_python_connector-1.2.0/tests/test_release_versioning.py +71 -0
  86. simind_python_connector-1.2.0/tests/test_schneider_density.py +104 -0
  87. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_simind_to_stir_converter.py +0 -43
  88. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_simulation_config.py +8 -44
  89. simind_python_connector-1.2.0/tests/test_single_window_simind.py +49 -0
  90. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_stir_backend_arithmetic.py +18 -0
  91. simind_python_connector-1.2.0/tests/test_utils.py +99 -0
  92. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_utils_small.py +4 -6
  93. simind_python_connector-1.1.2/simind_python_connector/connectors/base.py +0 -40
  94. simind_python_connector-1.1.2/tests/conftest.py +0 -100
  95. simind_python_connector-1.1.2/tests/test_arithmetic_operations.py +0 -106
  96. simind_python_connector-1.1.2/tests/test_integration.py +0 -38
  97. simind_python_connector-1.1.2/tests/test_schneider_density.py +0 -303
  98. simind_python_connector-1.1.2/tests/test_single_window_simind.py +0 -84
  99. simind_python_connector-1.1.2/tests/test_utils.py +0 -129
  100. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/.dockerignore +0 -0
  101. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/LICENSE +0 -0
  102. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/import_guard/sitecustomize.py +0 -0
  103. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docker/python/install_mpich_compat.sh +0 -0
  104. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/_static/.gitkeep +0 -0
  105. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/api.rst +0 -0
  106. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/backends.rst +0 -0
  107. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/connector_modernization_plan.rst +0 -0
  108. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/04_custom_config.py +0 -0
  109. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/06_schneider_density_conversion.py +0 -0
  110. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/08A_stir_adaptor_from_dicom.py +0 -0
  111. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/08B_sirf_adaptor_from_dicom.py +0 -0
  112. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/08C_pytomography_adaptor_from_dicom.py +0 -0
  113. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples/_python_connector_helpers.py +0 -0
  114. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/examples.rst +0 -0
  115. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/geometry.rst +0 -0
  116. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/index.rst +0 -0
  117. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/docs/intro.rst +0 -0
  118. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/04_custom_config.py +0 -0
  119. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/06_schneider_density_conversion.py +0 -0
  120. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/08A_stir_adaptor_from_dicom.py +0 -0
  121. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/08B_sirf_adaptor_from_dicom.py +0 -0
  122. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/08C_pytomography_adaptor_from_dicom.py +0 -0
  123. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/_python_connector_helpers.py +0 -0
  124. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/examples/run_all_examples.sh +0 -0
  125. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/scripts/fix.sh +0 -0
  126. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/scripts/run_all_examples.py +0 -0
  127. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/setup.cfg +0 -0
  128. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/configs/AnyScan.yaml +0 -0
  129. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/configs/Discovery670.yaml +0 -0
  130. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/configs/Example.yaml +0 -0
  131. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/configs/MLD001_SCAN0.yaml +0 -0
  132. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/configs/input.smc +0 -0
  133. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/connectors/__init__.py +2 -2
  134. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/converters/__init__.py +0 -0
  135. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/converters/dicom_to_stir.py +0 -0
  136. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/core/__init__.py +0 -0
  137. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/core/types.py +0 -0
  138. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/data/Schneider2000.json +0 -0
  139. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/data/__init__.py +0 -0
  140. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/data/bone.atn +0 -0
  141. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/data/h2o.atn +0 -0
  142. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/geometry_phantom.py +0 -0
  143. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector/utils/io_utils.py +0 -0
  144. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector.egg-info/dependency_links.txt +0 -0
  145. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector.egg-info/requires.txt +0 -0
  146. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/simind_python_connector.egg-info/top_level.txt +0 -0
  147. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/__init__.py +0 -0
  148. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/fixtures/minimal_variable_sections.smc +0 -0
  149. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_container_library_isolation.py +0 -0
  150. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_image_builder_unit.py +0 -0
  151. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_interfile_parser.py +0 -0
  152. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_patch_release_simind.py +0 -0
  153. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_sirf_stir_utils.py +0 -0
  154. {simind_python_connector-1.1.2 → simind_python_connector-1.2.0}/tests/test_stir_utils_backend_choice_unit.py +0 -0
@@ -11,12 +11,8 @@ recursive-include tests/fixtures *.smc
11
11
  recursive-include examples *.py *.sh
12
12
  recursive-include scripts *.py *.sh
13
13
  recursive-include docker Dockerfile *.md *.txt *.yaml *.yml *.py *.sh
14
- exclude AGENTS.md
15
- exclude opencode.jsonc
16
- exclude CLAUDE.md
17
14
  exclude simind_manual.pdf
18
15
  prune simind
19
- prune docs/superpowers
20
16
  prune docs/_build
21
17
  prune docs/_generated
22
18
  global-exclude __pycache__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simind-python-connector
3
- Version: 1.1.2
3
+ Version: 1.2.0
4
4
  Summary: Python SIMIND Monte Carlo connector with STIR/SIRF/PyTomography adaptors
5
5
  Author-email: Sam Porter <sam.porter.18@ucl.ac.uk>, Rebecca Gillen <rebecca.gillen.18@ucl.ac.uk>, "Efstathios (Stahis) Varzakis" <efstathios.varzakis.22@ucl.ac.uk>
6
6
  Maintainer-email: Sam Porter <sam.porter.18@ucl.ac.uk>
@@ -61,6 +61,9 @@ University.
61
61
  SIMIND is **not distributed** with this package. You must separately obtain and
62
62
  install a licensed SIMIND installation.
63
63
 
64
+ **Supported SIMIND version:** SIMIND V8.0. SIMIND V9.0 is not supported yet;
65
+ V9 support will follow in a later release.
66
+
64
67
  ## Quick Links
65
68
  - [Full Documentation](https://simind-python-connector.readthedocs.io/)
66
69
  - [Installation](https://simind-python-connector.readthedocs.io/en/latest/installation.html)
@@ -19,6 +19,9 @@ University.
19
19
  SIMIND is **not distributed** with this package. You must separately obtain and
20
20
  install a licensed SIMIND installation.
21
21
 
22
+ **Supported SIMIND version:** SIMIND V8.0. SIMIND V9.0 is not supported yet;
23
+ V9 support will follow in a later release.
24
+
22
25
  ## Quick Links
23
26
  - [Full Documentation](https://simind-python-connector.readthedocs.io/)
24
27
  - [Installation](https://simind-python-connector.readthedocs.io/en/latest/installation.html)
@@ -74,6 +74,9 @@ Services
74
74
  Notes
75
75
  -----
76
76
 
77
+ - The container scripts run the `python` service as the invoking host user
78
+ (`HOST_UID`/`HOST_GID`); direct `docker compose run python` runs as root.
79
+ The SIRF-based services always run as uid 1000 (`jovyan`).
77
80
  - The SIRF container build arg is `SIRF_BASE_IMAGE`.
78
81
  - The STIR container build arg is `STIR_BASE_IMAGE`.
79
82
  - Install SIMIND by following the official installation instructions on the SIMIND website.
@@ -4,8 +4,12 @@ services:
4
4
  context: ..
5
5
  dockerfile: docker/python/Dockerfile
6
6
  image: simind-python-connector/python:dev
7
+ # The container scripts run this service as the invoking host user so its
8
+ # outputs in the shared mount stay writable; direct use runs as root.
9
+ user: "${HOST_UID:-0}:${HOST_GID:-0}"
7
10
  working_dir: /workspace
8
11
  environment:
12
+ HOME: /tmp
9
13
  SIMIND_CONNECTOR_BACKEND: python
10
14
  SIMIND_ROOT: /workspace/simind
11
15
  SIMIND_BIN: ${SIMIND_BIN:-/workspace/simind/simind}
@@ -2,5 +2,7 @@
2
2
  # NumPy is capped below 2.x: the STIR/SIRF binaries in the synerbi/sirf base
3
3
  # images are compiled against NumPy 1.x and crash with NumPy 2 (see README
4
4
  # "NumPy ABI" notes).
5
+ # Backend installs also resolve the base image's imagecodecs and tifffile so
6
+ # their NumPy requirements stay compatible with this cap.
5
7
  numpy<2.0
6
8
  pytest==8.4.2
@@ -16,7 +16,7 @@ ENV PATH=${SIMIND_ROOT}/bin:${SIMIND_ROOT}:${PATH}
16
16
  # MPICH runtime for SIMIND's MPI binary; the compat script exposes the library
17
17
  # names that binary was built against. See docker/python/install_mpich_compat.sh.
18
18
  RUN apt-get update \
19
- && apt-get install -y --no-install-recommends mpich \
19
+ && apt-get install -y --no-install-recommends git mpich \
20
20
  && rm -rf /var/lib/apt/lists/*
21
21
 
22
22
  COPY docker/python/install_mpich_compat.sh /usr/local/bin/install_mpich_compat.sh
@@ -21,9 +21,8 @@ COPY --chown=1000:100 . /workspace
21
21
 
22
22
  RUN python -m pip install --upgrade pip \
23
23
  && pip install --index-url https://download.pytorch.org/whl/cpu torch \
24
- && pip install pytomography --no-deps \
25
- && pip install fft-conv-pytorch kornia pydicom rt-utils torchrbf uproot \
26
- && pip install --constraint /workspace/docker/constraints.txt -e ".[dev,examples]" \
24
+ && pip install --constraint /workspace/docker/constraints.txt \
25
+ -e ".[dev,examples]" pytomography imagecodecs tifffile \
27
26
  && python - <<'PY'
28
27
  import importlib
29
28
  for module in ("torch", "pytomography", "pytest"):
@@ -20,7 +20,8 @@ WORKDIR /workspace
20
20
  COPY --chown=1000:100 . /workspace
21
21
 
22
22
  RUN python -m pip install --upgrade pip \
23
- && pip install --constraint /workspace/docker/constraints.txt -e ".[dev,examples]" \
23
+ && pip install --constraint /workspace/docker/constraints.txt \
24
+ -e ".[dev,examples]" imagecodecs tifffile \
24
25
  && python - <<'PY'
25
26
  import importlib
26
27
  for module in ("sirf.STIR", "pytest"):
@@ -20,7 +20,8 @@ WORKDIR /workspace
20
20
  COPY --chown=1000:100 . /workspace
21
21
 
22
22
  RUN python -m pip install --upgrade pip \
23
- && pip install --constraint /workspace/docker/constraints.txt -e ".[dev,examples]" \
23
+ && pip install --constraint /workspace/docker/constraints.txt \
24
+ -e ".[dev,examples]" imagecodecs tifffile \
24
25
  && python - <<'PY'
25
26
  import stir # noqa: F401
26
27
  import pytest # noqa: F401
@@ -7,6 +7,64 @@ All notable changes to this project are documented in this file.
7
7
 
8
8
  The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_, and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
9
9
 
10
+ 1.2.0 - 2026-10-16
11
+ ------------------
12
+
13
+ Changed
14
+ ~~~~~~~
15
+
16
+ - **Breaking:** without the ``CA`` runtime switch, SCATTWIN runs return only
17
+ the total projection. The energy-window file now holds exactly the requested
18
+ windows; 1.1.x appended a scatter-order-1 line when every order was 0, which
19
+ made SIMIND also write ``sca``, ``air`` and a duplicate ``sca_w2``. Code that
20
+ reads ``outputs["sca_w1"]`` or ``get_scatter_output()`` needs
21
+ ``add_runtime_switch("CA", 1)``.
22
+ - Internal tidy-up with no behaviour change: type hints use ``X | None`` and
23
+ built-in generics, ``__all__`` lists are sorted, and redundant ``pass``
24
+ statements are removed.
25
+
26
+ Added
27
+ ~~~~~
28
+
29
+ - The ``CA`` runtime switch is accepted (``add_runtime_switch("CA", n)``). It
30
+ selects the SCATTWIN projections SIMIND V8 writes: without it only ``tot``;
31
+ ``1`` adds ``sca`` and ``air``; ``2`` adds ``pri``, ``sca`` and ``air``.
32
+ - A SCATTWIN run without the ``CA`` switch, and with every scatter order 0,
33
+ issues a ``UserWarning`` saying that only total projections will be written.
34
+ - ``get_primary_output()`` returns the primary projection written with
35
+ ``CA`` = 2.
36
+ - ``get_scatter_output()`` and ``get_primary_output()`` return the total minus
37
+ the other component when only that one was written, and warn and return
38
+ ``None`` when neither was.
39
+
40
+ Fixed
41
+ ~~~~~
42
+
43
+ - ``number - x`` on SIRF image and acquisition wrappers returned wrong values.
44
+ - ``StirImageData`` arithmetic raised ``TypeError`` on STIR 6.2, and
45
+ ``create_simple_phantom()`` and ``create_attenuation_map()`` failed there.
46
+ - ``StirAcquisitionData.get_info()`` returns STIR's parameter text instead of
47
+ the SWIG object's address.
48
+ - ``from simind_python_connector.utils import *`` raised ``AttributeError``.
49
+ - Interfile data with a partial trailing pixel is rejected instead of the extra
50
+ bytes being ignored.
51
+ - The DICOM acquisition builder no longer prints debug lines, and its warnings
52
+ name the missing ``AcquisitionDate`` and ``AcquisitionTime`` fields.
53
+
54
+ Deprecated
55
+ ~~~~~~~~~~
56
+
57
+ - STIR 6.2 and earlier, which have no image arithmetic. ``StirImageData``
58
+ computes it in NumPy there and issues a ``DeprecationWarning``; support will
59
+ be removed in a future release.
60
+
61
+ Notes
62
+ ~~~~~
63
+
64
+ - This release supports SIMIND V8.0. SIMIND V9.0 is not supported yet: it
65
+ changes several output formats and behaviours the connector relies on, so
66
+ V9 support will follow in a later release.
67
+
10
68
  1.1.2 - 2026-10-15
11
69
  ------------------
12
70
 
@@ -3,7 +3,8 @@
3
3
  import os
4
4
  import sys
5
5
  from datetime import datetime
6
- from importlib import metadata as importlib_metadata
6
+ from pathlib import Path
7
+ from runpy import run_path
7
8
 
8
9
 
9
10
  # Add the package to the path
@@ -13,13 +14,9 @@ sys.path.insert(0, os.path.abspath(".."))
13
14
  project = "simind-python-connector"
14
15
  copyright = f"{datetime.now().year}, Sam Porter, Efstathios Varzakis"
15
16
  author = "Sam Porter, Efstathios Varzakis"
16
- release = "1.1.2"
17
- for dist_name in ("simind-python-connector",):
18
- try:
19
- release = importlib_metadata.version(dist_name)
20
- break
21
- except importlib_metadata.PackageNotFoundError:
22
- continue
17
+ release = run_path(
18
+ str(Path(__file__).resolve().parents[1] / "simind_python_connector/_version.py")
19
+ )["__version__"]
23
20
  version = ".".join(release.split(".")[:2])
24
21
 
25
22
  # General configuration
@@ -44,3 +44,24 @@ If you make changes that affect the documentation:
44
44
  .. code-block:: bash
45
45
 
46
46
  sphinx-build -b html docs docs/_build/html
47
+
48
+ Preparing a Release
49
+ -------------------
50
+
51
+ The version is defined once in ``simind_python_connector/_version.py``.
52
+ Package builds, ``simind_python_connector.__version__``, documentation and the
53
+ publishing workflow all read it directly.
54
+
55
+ Write the release notes for the next version in ``CHANGELOG.md`` and
56
+ ``docs/changelog.rst``, then run:
57
+
58
+ .. code-block:: bash
59
+
60
+ bash scripts/release.sh patch
61
+
62
+ Use ``minor``, ``major`` or an exact version such as ``1.2.0`` instead of
63
+ ``patch`` when appropriate. The script updates the shared version and the
64
+ Zenodo and citation metadata, including the citation release date, then creates
65
+ a local release commit and tag. Review your working tree first; the script also
66
+ commits the release notes. It prints the commands for pushing and publishing.
67
+ No other version values need editing by hand.
@@ -48,10 +48,11 @@ def main() -> None:
48
48
  connector.add_config_value(53, 0)
49
49
  connector.set_energy_windows([126.0], [154.0], [0])
50
50
  add_standard_runtime(connector, photon_multiplier=1, seed=12345, nuclide="tc99m")
51
+ connector.add_runtime_switch("CA", 1) # also write scatter and air projections
51
52
 
52
53
  outputs = connector.run()
53
54
  total = outputs["tot_w1"].projection
54
- scatter = outputs.get("sca_w1", outputs["tot_w1"]).projection
55
+ scatter = outputs["sca_w1"].projection
55
56
  primary = np.clip(total - scatter, a_min=0.0, a_max=None)
56
57
 
57
58
  np.save(output_dir / "total.npy", total)
@@ -47,6 +47,7 @@ def _run_case(
47
47
  connector.add_config_value(53, 0)
48
48
  connector.set_energy_windows([75.0], [225.0], [0])
49
49
  add_standard_runtime(connector, photon_multiplier=1, seed=seed)
50
+ connector.add_runtime_switch("CA", 0) # total projections only
50
51
  outputs = connector.run()
51
52
  return outputs["tot_w1"].projection
52
53
 
@@ -51,6 +51,7 @@ def main() -> None:
51
51
  connector.set_energy_windows(lowers, uppers, orders)
52
52
  connector.add_config_value(1, 208.0)
53
53
  add_standard_runtime(connector, photon_multiplier=1, seed=12345, nuclide="lu177")
54
+ connector.add_runtime_switch("CA", 0) # total projections only
54
55
 
55
56
  outputs = connector.run()
56
57
 
@@ -47,6 +47,10 @@ def _run_case(
47
47
  connector.add_config_value(53, 1)
48
48
  connector.set_energy_windows([75.0], [225.0], [0])
49
49
  add_standard_runtime(connector, photon_multiplier=1, seed=12345)
50
+ # CA = 1 makes SCATTWIN write scatter and air projections; PENETRATE would
51
+ # drop its per-component outputs with it.
52
+ if scoring_routine == 1:
53
+ connector.add_runtime_switch("CA", 1)
50
54
 
51
55
  results = connector.run()
52
56
  return {key: value.projection for key, value in results.items()}
@@ -401,8 +405,8 @@ def main() -> None:
401
405
  )
402
406
  pen_outputs = _run_case(pen_dir, "pen_case01", source, mu_map, scoring_routine=4)
403
407
 
404
- scatt_total = scatt_outputs.get("tot_w1", scatt_outputs[sorted(scatt_outputs)[0]])
405
- scatt_scatter = scatt_outputs.get("sca_w1", np.zeros_like(scatt_total))
408
+ scatt_total = scatt_outputs["tot_w1"]
409
+ scatt_scatter = scatt_outputs["sca_w1"]
406
410
  scatt_primary = np.clip(scatt_total - scatt_scatter, a_min=0.0, a_max=None)
407
411
  scatt_primary_sum = float(scatt_primary.sum())
408
412
  scatt_scatter_sum = float(scatt_scatter.sum())
@@ -177,6 +177,7 @@ def main() -> None:
177
177
  adaptor.add_runtime_switch("CC", "ma-megp")
178
178
  adaptor.add_runtime_switch("NN", 1)
179
179
  adaptor.add_runtime_switch("RR", 12345)
180
+ adaptor.add_runtime_switch("CA", 0) # total projections only
180
181
  adaptor.add_config_value(1, 140.0)
181
182
  adaptor.add_config_value(19, 2)
182
183
  adaptor.add_config_value(53, 0)
@@ -159,6 +159,7 @@ def main() -> None:
159
159
  adaptor.add_runtime_switch("CC", "ma-megp")
160
160
  adaptor.add_runtime_switch("NN", 1)
161
161
  adaptor.add_runtime_switch("RR", 12345)
162
+ adaptor.add_runtime_switch("CA", 0) # total projections only
162
163
  adaptor.add_config_value(1, 140.0)
163
164
  adaptor.add_config_value(19, 2)
164
165
  adaptor.add_config_value(53, 0)
@@ -127,6 +127,7 @@ def main() -> None:
127
127
  adaptor.add_runtime_switch("CC", "ma-megp")
128
128
  adaptor.add_runtime_switch("NN", 1)
129
129
  adaptor.add_runtime_switch("RR", 12345)
130
+ adaptor.add_runtime_switch("CA", 0) # total projections only
130
131
  adaptor.add_config_value(1, 140.0)
131
132
  adaptor.add_config_value(19, 2)
132
133
  adaptor.add_config_value(53, 0)
@@ -65,6 +65,16 @@ maintained by** the SIMIND project or Lund University.
65
65
  SIMIND is **not** distributed with this package (or on PyPI) and must be
66
66
  installed separately.
67
67
 
68
+ Supported SIMIND Versions
69
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
70
+
71
+ This release supports and is tested with **SIMIND V8.0**.
72
+
73
+ **SIMIND V9.0 is not supported yet.** V9 changes several output formats and
74
+ behaviours the connector relies on, so runs can fail or return incorrect
75
+ projections. V9 support will follow in a later release; until then, use
76
+ SIMIND V8.0.
77
+
68
78
  Use the official SIMIND resources for installation and manual/reference
69
79
  documentation:
70
80
 
@@ -29,14 +29,15 @@ Automatic Dependency Detection
29
29
 
30
30
  The test suite automatically detects available dependencies:
31
31
 
32
- * **SIRF detection**: Attempts to ``import sirf``
32
+ * **SIRF detection**: Attempts to ``import sirf.STIR``
33
33
  * **STIR detection**: Attempts to ``import stir``, then imports the STIR
34
34
  conversion helpers the way the package itself does (``stir.extra`` preferred,
35
35
  the legacy ``stirextra`` module only as a fallback), so a broken
36
36
  ``stir.extra`` dependency is reported as unavailable instead of being masked
37
37
  by the legacy module
38
38
  * **PyTomography detection**: Attempts to ``import pytomography``
39
- * **SIMIND detection**: Checks if ``simind`` command is available
39
+ * **SIMIND detection**: Uses ``$SIMIND_BIN`` when set, otherwise looks for
40
+ ``simind`` on ``PATH``
40
41
  * **CI detection**: Checks ``CI`` or ``GITHUB_ACTIONS`` environment variables
41
42
 
42
43
  Tests requiring unavailable dependencies are automatically skipped with informative messages.
@@ -118,17 +119,6 @@ the official SIMIND site and manual:
118
119
  The container suite includes ``tests/test_container_library_isolation.py``,
119
120
  which asserts that each container can import only its target backend library.
120
121
 
121
- Alternative CI Configuration
122
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123
-
124
- The dependency-exclusion selection lives in ``.github/workflows/tests.yml`` so it
125
- applies to CI only; the default ``pytest`` run keeps every available test.
126
-
127
- .. code-block:: bash
128
-
129
- # Run only the CI-friendly selection locally
130
- pytest -m "not integration and not requires_sirf and not requires_stir and not requires_simind and not requires_pytomography and not requires_cil and not requires_setr and not ci_skip"
131
-
132
122
  Test Categories
133
123
  ---------------
134
124
 
@@ -140,8 +130,8 @@ The default CI job runs the tests that avoid heavyweight dependencies. These foc
140
130
  * **Conversion maths** – ``tests/test_schneider_density.py`` exercises the HU-to-density pipelines.
141
131
  * **Configuration & builders** – ``tests/test_simulation_config.py`` and ``tests/test_acquisition_builder_unit.py`` validate YAML/SMC handling plus Interfile header generation without a SIRF runtime.
142
132
  * **Connector behavior** – ``tests/test_python_connector.py`` and ``tests/test_connector_backend_separation.py`` cover the connector-first API without requiring reconstruction packages.
143
- * **Utility helpers** – ``tests/test_utils.py``, ``tests/test_utils_small.py``, ``tests/test_interfile_parser.py``, and ``tests/test_interfile_numpy.py`` cover file parsing and small helper functions.
144
- * **Backend guards** – ``tests/test_backends.py`` ensures optional backend imports remain isolated.
133
+ * **Utility helpers** – ``tests/test_utils_small.py``, ``tests/test_interfile_parser.py``, and ``tests/test_interfile_numpy.py`` cover file parsing and small helper functions.
134
+ * **Backend dispatch** – ``tests/test_backends.py`` covers backend detection and dispatch with stand-in backends.
145
135
 
146
136
  Backend-Dependent Suites
147
137
  ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -149,15 +139,16 @@ Backend-Dependent Suites
149
139
  Tests that construct real STIR/SIRF/PyTomography objects carry dependency
150
140
  markers and are skipped when those packages are unavailable. These include:
151
141
 
152
- * ``tests/test_native_adaptors.py`` for STIR/SIRF adaptor behavior.
142
+ * ``tests/test_native_geometry_real.py`` for native STIR/SIRF image geometry.
143
+ * ``tests/test_utils.py`` for phantom, image and acquisition helpers on SIRF and STIR.
153
144
  * ``tests/test_pytomography_adaptor.py`` for PyTomography tensor and output adaptation.
154
- * ``tests/test_arithmetic_operations.py`` and ``tests/test_sirf_stir_utils.py`` for backend wrapper/helper behavior.
145
+ * ``tests/test_arithmetic_operations.py`` and ``tests/test_stir_backend_arithmetic.py`` for arithmetic on real backend objects.
155
146
 
156
147
  SIMIND and Container Diagnostics
157
148
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
158
149
 
159
- SIMIND-dependent tests use ``requires_simind`` and are skipped unless the
160
- ``simind`` command is available. Container and geometry diagnostics include:
150
+ SIMIND-dependent tests use ``requires_simind`` and are skipped unless SIMIND
151
+ is found. Container and geometry diagnostics include:
161
152
 
162
153
  * ``tests/test_integration.py`` for full workflow checks.
163
154
  * ``tests/test_container_library_isolation.py`` for backend container import isolation.
@@ -168,13 +159,6 @@ latter two are provided by ``docker/python/Dockerfile``, which installs MPICH
168
159
  and exposes the ``libmpi.so.12``/``libmpifort.so.12`` names SIMIND links
169
160
  against. They never run in GitHub CI because the SIMIND binary is not tracked.
170
161
 
171
- Integration Test
172
- ~~~~~~~~~~~~~~~~
173
-
174
- ``tests/test_integration.py`` orchestrates a full example run and needs
175
- external runtime dependencies. It is marked with dependency markers so it is
176
- skipped in lightweight CI environments.
177
-
178
162
  Configuration Files
179
163
  -------------------
180
164
 
@@ -195,34 +179,19 @@ tests/conftest.py
195
179
  Adding New Tests
196
180
  ----------------
197
181
 
198
- When adding new tests, use appropriate markers:
199
-
200
- .. code-block:: python
201
-
202
- import pytest
203
-
204
- @pytest.mark.unit
205
- def test_pure_python_logic():
206
- """Test that doesn't need external dependencies."""
207
- assert True
182
+ Use ``unit`` for isolated behavior and ``integration`` for workflows involving
183
+ external systems. Add the relevant dependency markers whenever a test needs a
184
+ reconstruction library or SIMIND. The distribution build check runs once in
185
+ the CI build job, separately from the unit test matrix.
208
186
 
209
- @pytest.mark.requires_sirf
210
- def test_sirf_functionality():
211
- """Test that uses SIRF objects."""
212
- from sirf.STIR import ImageData
213
- # ... test code
187
+ Assert observable results: array values and orientation, saved file contents,
188
+ metadata or a useful failure. Stub external I/O at the boundary when needed.
189
+ Tests that only check whether a function exists, repeat its implementation or
190
+ assert private storage details do not establish that a workflow works.
214
191
 
215
- @pytest.mark.requires_simind
216
- def test_simind_execution():
217
- """Test that calls SIMIND command."""
218
- # ... test code that runs simind
219
-
220
- @pytest.mark.requires_pytomography
221
- def test_pytomography_path():
222
- """Test that uses PyTomography APIs."""
223
- # ... test code that imports pytomography
224
-
225
- This ensures your tests will be properly categorized and run in the appropriate environments.
192
+ SIMIND geometry comparisons use Flag 8 set to false for a repeatable initial
193
+ random state. Choose photon statistics to support the numerical tolerance;
194
+ setting ``RR`` alone does not make V8 simulations reproducible.
226
195
 
227
196
  Continuous Integration
228
197
  ----------------------
@@ -234,5 +203,3 @@ GitHub Actions is used to run tests automatically. The CI workflow:
234
203
  3. **Executes CI-friendly tests** using dependency markers
235
204
  4. **Generates coverage reports** for the tested code
236
205
  5. **Builds and validates the package**
237
-
238
- This approach ensures reliable CI while maintaining comprehensive test coverage for local development.
@@ -61,12 +61,10 @@ SPECT header remains. Measured on a real SIMIND V8.0 run, 49 characters is the
61
61
  exact maximum prefix for ``{prefix}_tot_w1.a00``; a 50-character prefix
62
62
  truncates it. The 47-character cap is a conservative policy with a small margin
63
63
  below that measured boundary, not an engine-measured limit. The longest
64
- engine-written name adds a ``_tot_w{N}.a00`` suffix (10 characters plus the
65
- digits of the window number), and when every scatter order is below 1 the
66
- window file gains the required extra line, which adds one more output set, so
67
- the suffix can reach 13 characters and a 47-character prefix plus that suffix
68
- uses the 60-character buffer exactly. To stay inside that budget, at most 99
69
- energy windows are accepted; a larger set raises ``ValueError``.
64
+ engine-written name adds a ``_tot_w{N}.a00`` suffix. With at most 99 energy
65
+ windows this suffix is 12 characters long, leaving a margin inside the
66
+ 60-character buffer. Window files contain exactly the requested windows;
67
+ more than 99 windows raises ``ValueError``.
70
68
 
71
69
  ``RuntimeOperator`` carries per-run switches, an orbit file and an optional
72
70
  timeout in seconds. A run that exceeds the timeout stops and raises
@@ -116,6 +114,11 @@ supported by the Interfile connector. LIST_MODE (2) and FORCED_COLLIMATION (3)
116
114
  write outputs the connector cannot read, so they are rejected at preflight;
117
115
  they remain available through lower-level use of the package.
118
116
 
117
+ For repeatable diagnostic runs on SIMIND V8, set
118
+ ``connector.get_config().set_flag(8, False)`` to reuse the same initial random
119
+ state. The bundled configurations enable Flag 8 for independent simulations.
120
+ The ``RR`` switch alone does not make repeated runs reproducible.
121
+
119
122
  A literal 1x1x1 voxel phantom is rejected (SIMIND does not support it), so pad
120
123
  the source and mu map to at least two voxels in one dimension. A non-zero
121
124
  source that ``quantization_scale`` rounds to all zeros is also rejected;
@@ -136,12 +139,28 @@ Adaptor Workflows
136
139
  Use adaptors when you want connector-managed SIMIND execution plus native
137
140
  objects for a target reconstruction package.
138
141
 
139
- Primary projections are unsupported by these adaptors. ``get_primary_output()``
140
- raises ``NotImplementedError`` once outputs exist and ``RuntimeError`` before a
141
- successful run. The adaptors consume SIMIND's standard SPECT output, which has
142
- no standalone primary file; primary projections can still be produced through
143
- lower-level SIMIND use with ``/CA:2``. Use ``get_total_output()`` and
144
- ``get_scatter_output()`` instead.
142
+ Enabling ``keep_all_views_in_cache`` in SIRF/STIR can cause memory errors on
143
+ large acquisitions. Disable it if view caching exhausts available memory.
144
+
145
+ With the SCATTWIN scoring routine, SIMIND V8 writes only the total projection
146
+ unless the ``CA`` runtime switch asks for more: ``CA`` = 1 adds scatter
147
+ (``sca``) and air (``air``) projections, ``CA`` = 2 adds primary (``pri``),
148
+ scatter and air projections. A SCATTWIN run without ``CA`` issues a
149
+ ``UserWarning``; set ``CA`` = 0 to keep total-only output without it.
150
+
151
+ Keep energy-window scatter orders at 0 unless you need SIMIND's per-order
152
+ behaviour. On V8, a window with scatter order above 0 produces only
153
+ ``sca_wN`` (no ``tot_wN``), and its presence makes SIMIND also write ``sca``
154
+ and ``air`` for the order-0 windows whatever ``CA`` is set to.
155
+
156
+ .. code-block:: python
157
+
158
+ adaptor.add_runtime_switch("CA", 1)
159
+
160
+ ``get_scatter_output()`` and ``get_primary_output()`` return the written
161
+ projection, or the total minus the other one when only that was written
162
+ (``tot = pri + sca``). When neither was written they warn and return ``None``.
163
+ Before a successful run they raise ``RuntimeError``.
145
164
 
146
165
  STIR adaptor
147
166
  ~~~~~~~~~~~~
@@ -48,10 +48,11 @@ def main() -> None:
48
48
  connector.add_config_value(53, 0)
49
49
  connector.set_energy_windows([126.0], [154.0], [0])
50
50
  add_standard_runtime(connector, photon_multiplier=1, seed=12345, nuclide="tc99m")
51
+ connector.add_runtime_switch("CA", 1) # also write scatter and air projections
51
52
 
52
53
  outputs = connector.run()
53
54
  total = outputs["tot_w1"].projection
54
- scatter = outputs.get("sca_w1", outputs["tot_w1"]).projection
55
+ scatter = outputs["sca_w1"].projection
55
56
  primary = np.clip(total - scatter, a_min=0.0, a_max=None)
56
57
 
57
58
  np.save(output_dir / "total.npy", total)
@@ -47,6 +47,7 @@ def _run_case(
47
47
  connector.add_config_value(53, 0)
48
48
  connector.set_energy_windows([75.0], [225.0], [0])
49
49
  add_standard_runtime(connector, photon_multiplier=1, seed=seed)
50
+ connector.add_runtime_switch("CA", 0) # total projections only
50
51
  outputs = connector.run()
51
52
  return outputs["tot_w1"].projection
52
53
 
@@ -51,6 +51,7 @@ def main() -> None:
51
51
  connector.set_energy_windows(lowers, uppers, orders)
52
52
  connector.add_config_value(1, 208.0)
53
53
  add_standard_runtime(connector, photon_multiplier=1, seed=12345, nuclide="lu177")
54
+ connector.add_runtime_switch("CA", 0) # total projections only
54
55
 
55
56
  outputs = connector.run()
56
57
 
@@ -47,6 +47,10 @@ def _run_case(
47
47
  connector.add_config_value(53, 1)
48
48
  connector.set_energy_windows([75.0], [225.0], [0])
49
49
  add_standard_runtime(connector, photon_multiplier=1, seed=12345)
50
+ # CA = 1 makes SCATTWIN write scatter and air projections; PENETRATE would
51
+ # drop its per-component outputs with it.
52
+ if scoring_routine == 1:
53
+ connector.add_runtime_switch("CA", 1)
50
54
 
51
55
  results = connector.run()
52
56
  return {key: value.projection for key, value in results.items()}
@@ -401,8 +405,8 @@ def main() -> None:
401
405
  )
402
406
  pen_outputs = _run_case(pen_dir, "pen_case01", source, mu_map, scoring_routine=4)
403
407
 
404
- scatt_total = scatt_outputs.get("tot_w1", scatt_outputs[sorted(scatt_outputs)[0]])
405
- scatt_scatter = scatt_outputs.get("sca_w1", np.zeros_like(scatt_total))
408
+ scatt_total = scatt_outputs["tot_w1"]
409
+ scatt_scatter = scatt_outputs["sca_w1"]
406
410
  scatt_primary = np.clip(scatt_total - scatt_scatter, a_min=0.0, a_max=None)
407
411
  scatt_primary_sum = float(scatt_primary.sum())
408
412
  scatt_scatter_sum = float(scatt_scatter.sum())
@@ -177,6 +177,7 @@ def main() -> None:
177
177
  adaptor.add_runtime_switch("CC", "ma-megp")
178
178
  adaptor.add_runtime_switch("NN", 1)
179
179
  adaptor.add_runtime_switch("RR", 12345)
180
+ adaptor.add_runtime_switch("CA", 0) # total projections only
180
181
  adaptor.add_config_value(1, 140.0)
181
182
  adaptor.add_config_value(19, 2)
182
183
  adaptor.add_config_value(53, 0)
@@ -159,6 +159,7 @@ def main() -> None:
159
159
  adaptor.add_runtime_switch("CC", "ma-megp")
160
160
  adaptor.add_runtime_switch("NN", 1)
161
161
  adaptor.add_runtime_switch("RR", 12345)
162
+ adaptor.add_runtime_switch("CA", 0) # total projections only
162
163
  adaptor.add_config_value(1, 140.0)
163
164
  adaptor.add_config_value(19, 2)
164
165
  adaptor.add_config_value(53, 0)
@@ -127,6 +127,7 @@ def main() -> None:
127
127
  adaptor.add_runtime_switch("CC", "ma-megp")
128
128
  adaptor.add_runtime_switch("NN", 1)
129
129
  adaptor.add_runtime_switch("RR", 12345)
130
+ adaptor.add_runtime_switch("CA", 0) # total projections only
130
131
  adaptor.add_config_value(1, 140.0)
131
132
  adaptor.add_config_value(19, 2)
132
133
  adaptor.add_config_value(53, 0)