From 4f851652e115f78398c7223d6a3458edbf2008ba Mon Sep 17 00:00:00 2001 From: Sam Porter <92305641+samdporter@users.noreply.github.com> Date: Mon, 9 Mar 2026 11:20:20 +0000 Subject: [PATCH 1/3] feat: support attenuation/density/HU phantom map inputs --- README.md | 8 ++ .../connectors/python_connector.py | 46 ++++++++++- .../connectors/pytomography_adaptor.py | 4 + .../connectors/sirf_adaptor.py | 4 + .../connectors/stir_adaptor.py | 4 + tests/test_native_adaptors.py | 14 +++- tests/test_python_connector.py | 80 +++++++++++++++++++ tests/test_pytomography_adaptor.py | 4 + 8 files changed, 158 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8ea9db0..1e40429 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ connector.configure_voxel_phantom( source=source, mu_map=mu_map, voxel_size_mm=4.0, + mu_map_type="attenuation", # or "density" / "hu" ) connector.set_energy_windows([126], [154], [0]) # Tc-99m ± 10% connector.add_runtime_switch("FI", "tc99m") @@ -159,6 +160,13 @@ total = outputs["tot_w1"].projection print(total.shape) ``` +`mu_map_type` controls how `mu_map` is interpreted before writing SIMIND +density input: + +- `"attenuation"`: linear attenuation coefficients (cm^-1), converted to density +- `"density"`: density map in g/cm^3, used directly +- `"hu"`: CT HU map, converted with the Schneider model + ### Advanced Density Conversion ```python diff --git a/sirf_simind_connection/connectors/python_connector.py b/sirf_simind_connection/connectors/python_connector.py index e00c6f2..aa1ca94 100644 --- a/sirf_simind_connection/connectors/python_connector.py +++ b/sirf_simind_connection/connectors/python_connector.py @@ -10,12 +10,15 @@ import shutil from dataclasses import dataclass, field from pathlib import Path -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Literal, Optional, Union import numpy as np from sirf_simind_connection.connectors.base import BaseConnector -from sirf_simind_connection.converters.attenuation import attenuation_to_density +from sirf_simind_connection.converters.attenuation import ( + attenuation_to_density, + hu_to_density_schneider, +) from sirf_simind_connection.converters.simind_to_stir import SimindToStirConverter from sirf_simind_connection.core.config import RuntimeSwitches, SimulationConfig from sirf_simind_connection.core.executor import SimindExecutor @@ -31,6 +34,7 @@ ConfigSource = Union[str, os.PathLike[str], SimulationConfig] PathLike = Union[str, os.PathLike[str]] +MuMapType = Literal["attenuation", "density", "hu"] @dataclass(frozen=True) @@ -108,15 +112,31 @@ def configure_voxel_phantom( mu_map: np.ndarray, voxel_size_mm: float = 4.0, scoring_routine: Union[ScoringRoutine, int] = ScoringRoutine.SCATTWIN, + mu_map_type: MuMapType = "attenuation", ) -> tuple[Path, Path]: """ Configure voxel geometry and write source/density input files. + Args: + source: Source activity array in SIMIND image axes (z, y, x). + mu_map: Input volume interpreted according to ``mu_map_type``. + voxel_size_mm: Isotropic voxel size in mm. + scoring_routine: SIMIND scoring routine identifier. + mu_map_type: Interpretation of ``mu_map`` values: + - ``"attenuation"``: linear attenuation (cm^-1), converted to density. + - ``"density"``: density (g/cm^3), written directly. + - ``"hu"``: CT Hounsfield Units, converted via Schneider model. + Returns: Tuple of (source_file_path, density_file_path). """ source_array = np.asarray(source, dtype=np.float32) mu_map_array = np.asarray(mu_map, dtype=np.float32) + normalized_map_type = str(mu_map_type).strip().lower() + if normalized_map_type not in {"attenuation", "density", "hu"}: + raise ValueError( + "mu_map_type must be one of: 'attenuation', 'density', 'hu'" + ) if source_array.ndim != 3 or mu_map_array.ndim != 3: raise ValueError("source and mu_map must both be 3D arrays") @@ -176,8 +196,11 @@ def configure_voxel_phantom( cfg.set_data_file(6, src_prefix) if cfg.get_flag(11): - photon_energy = float(cfg.get_value("photon_energy")) - density = attenuation_to_density(mu_map_array, photon_energy) * 1000.0 + density = self._convert_mu_input_to_density( + mu_map_array=mu_map_array, + mu_map_type=normalized_map_type, + photon_energy=float(cfg.get_value("photon_energy")), + ) else: density = np.zeros_like(mu_map_array) @@ -191,6 +214,21 @@ def configure_voxel_phantom( return source_path, density_path + @staticmethod + def _convert_mu_input_to_density( + mu_map_array: np.ndarray, + mu_map_type: MuMapType, + photon_energy: float, + ) -> np.ndarray: + """Convert attenuation/HU/density map input to density in mg/cm^3.""" + if mu_map_type == "attenuation": + density_g_cm3 = attenuation_to_density(mu_map_array, photon_energy) + elif mu_map_type == "density": + density_g_cm3 = mu_map_array + else: # mu_map_type == "hu" + density_g_cm3 = hu_to_density_schneider(mu_map_array) + return density_g_cm3 * 1000.0 + def set_energy_windows( self, lower_bounds: Union[float, list[float]], diff --git a/sirf_simind_connection/connectors/pytomography_adaptor.py b/sirf_simind_connection/connectors/pytomography_adaptor.py index 286e19b..b449058 100644 --- a/sirf_simind_connection/connectors/pytomography_adaptor.py +++ b/sirf_simind_connection/connectors/pytomography_adaptor.py @@ -17,6 +17,7 @@ from sirf_simind_connection.connectors.base import BaseConnector from sirf_simind_connection.connectors.python_connector import ( ConfigSource, + MuMapType, RuntimeOperator, SimindPythonConnector, ) @@ -56,6 +57,7 @@ def __init__( voxel_size_mm: float = 4.0, quantization_scale: float = 1.0, scoring_routine: Union[ScoringRoutine, int] = ScoringRoutine.SCATTWIN, + mu_map_type: MuMapType = "attenuation", ) -> None: if torch is None: raise ImportError( @@ -80,6 +82,7 @@ def __init__( if isinstance(scoring_routine, int) else scoring_routine ) + self._mu_map_type: MuMapType = mu_map_type self._source: Optional[torch.Tensor] = None self._mu_map: Optional[torch.Tensor] = None @@ -146,6 +149,7 @@ def run( mu_map=mu_map_zyx, voxel_size_mm=self.voxel_size_mm, scoring_routine=self._scoring_routine, + mu_map_type=self._mu_map_type, ) self.python_connector.set_energy_windows(*self._energy_windows) diff --git a/sirf_simind_connection/connectors/sirf_adaptor.py b/sirf_simind_connection/connectors/sirf_adaptor.py index e042e3d..d926853 100644 --- a/sirf_simind_connection/connectors/sirf_adaptor.py +++ b/sirf_simind_connection/connectors/sirf_adaptor.py @@ -10,6 +10,7 @@ from sirf_simind_connection.connectors.base import BaseConnector from sirf_simind_connection.connectors.python_connector import ( ConfigSource, + MuMapType, RuntimeOperator, SimindPythonConnector, ) @@ -35,6 +36,7 @@ def __init__( photon_multiplier: int = 1, quantization_scale: float = 1.0, scoring_routine: ScoringRoutine | int = ScoringRoutine.SCATTWIN, + mu_map_type: MuMapType = "attenuation", ) -> None: if sirf is None: raise ImportError("SirfSimindAdaptor requires the SIRF Python package.") @@ -53,6 +55,7 @@ def __init__( self._source: Any = None self._mu_map: Any = None self._outputs: Optional[dict[str, Any]] = None + self._mu_map_type: MuMapType = mu_map_type self.add_runtime_switch("NN", photon_multiplier) @@ -92,6 +95,7 @@ def run(self, runtime_operator: Optional[RuntimeOperator] = None) -> dict[str, A mu_map=mu_arr, voxel_size_mm=voxel_size_mm, scoring_routine=self._scoring_routine, + mu_map_type=self._mu_map_type, ) raw_outputs = self.python_connector.run(runtime_operator=runtime_operator) self._outputs = { diff --git a/sirf_simind_connection/connectors/stir_adaptor.py b/sirf_simind_connection/connectors/stir_adaptor.py index 38fe985..e317b71 100644 --- a/sirf_simind_connection/connectors/stir_adaptor.py +++ b/sirf_simind_connection/connectors/stir_adaptor.py @@ -10,6 +10,7 @@ from sirf_simind_connection.connectors.base import BaseConnector from sirf_simind_connection.connectors.python_connector import ( ConfigSource, + MuMapType, RuntimeOperator, SimindPythonConnector, ) @@ -35,6 +36,7 @@ def __init__( photon_multiplier: int = 1, quantization_scale: float = 1.0, scoring_routine: ScoringRoutine | int = ScoringRoutine.SCATTWIN, + mu_map_type: MuMapType = "attenuation", ) -> None: if stir is None: raise ImportError("StirSimindAdaptor requires the STIR Python package.") @@ -53,6 +55,7 @@ def __init__( self._source: Any = None self._mu_map: Any = None self._outputs: Optional[dict[str, Any]] = None + self._mu_map_type: MuMapType = mu_map_type self.add_runtime_switch("NN", photon_multiplier) @@ -92,6 +95,7 @@ def run(self, runtime_operator: Optional[RuntimeOperator] = None) -> dict[str, A mu_map=mu_arr, voxel_size_mm=voxel_size_mm, scoring_routine=self._scoring_routine, + mu_map_type=self._mu_map_type, ) raw_outputs = self.python_connector.run(runtime_operator=runtime_operator) self._outputs = { diff --git a/tests/test_native_adaptors.py b/tests/test_native_adaptors.py index ee61ac6..3387a40 100644 --- a/tests/test_native_adaptors.py +++ b/tests/test_native_adaptors.py @@ -146,6 +146,7 @@ def test_stir_adaptor_run_forwards_expected_connector_inputs( output_dir=str(tmp_path), output_prefix="case01", scoring_routine=ScoringRoutine.PENETRATE, + mu_map_type="density", ) source_arr = np.arange(2 * 3 * 4, dtype=np.float64).reshape(2, 3, 4) @@ -157,11 +158,14 @@ def test_stir_adaptor_run_forwards_expected_connector_inputs( captured: dict[str, object] = {} - def fake_configure_voxel_phantom(source, mu_map, voxel_size_mm, scoring_routine): + def fake_configure_voxel_phantom( + source, mu_map, voxel_size_mm, scoring_routine, mu_map_type + ): captured["source"] = source captured["mu_map"] = mu_map captured["voxel_size_mm"] = voxel_size_mm captured["scoring_routine"] = scoring_routine + captured["mu_map_type"] = mu_map_type return (tmp_path / "case01_src.smi", tmp_path / "case01_dns.dmi") def fake_run(runtime_operator=None): @@ -185,6 +189,7 @@ def fake_run(runtime_operator=None): assert np.asarray(captured["mu_map"]).shape == (2, 3, 4) assert captured["voxel_size_mm"] == pytest.approx(4.25) assert captured["scoring_routine"] == ScoringRoutine.PENETRATE + assert captured["mu_map_type"] == "density" assert captured["runtime_operator"] is runtime_operator @@ -294,6 +299,7 @@ def test_sirf_adaptor_run_forwards_expected_connector_inputs( output_dir=str(tmp_path), output_prefix="case01", scoring_routine=ScoringRoutine.PENETRATE, + mu_map_type="hu", ) source_arr = np.arange(2 * 3 * 4, dtype=np.float64).reshape(2, 3, 4) @@ -305,11 +311,14 @@ def test_sirf_adaptor_run_forwards_expected_connector_inputs( captured: dict[str, object] = {} - def fake_configure_voxel_phantom(source, mu_map, voxel_size_mm, scoring_routine): + def fake_configure_voxel_phantom( + source, mu_map, voxel_size_mm, scoring_routine, mu_map_type + ): captured["source"] = source captured["mu_map"] = mu_map captured["voxel_size_mm"] = voxel_size_mm captured["scoring_routine"] = scoring_routine + captured["mu_map_type"] = mu_map_type return (tmp_path / "case01_src.smi", tmp_path / "case01_dns.dmi") def fake_run(runtime_operator=None): @@ -333,6 +342,7 @@ def fake_run(runtime_operator=None): assert np.asarray(captured["mu_map"]).shape == (2, 3, 4) assert captured["voxel_size_mm"] == pytest.approx(3.75) assert captured["scoring_routine"] == ScoringRoutine.PENETRATE + assert captured["mu_map_type"] == "hu" assert captured["runtime_operator"] is runtime_operator diff --git a/tests/test_python_connector.py b/tests/test_python_connector.py index 38d90af..6114346 100644 --- a/tests/test_python_connector.py +++ b/tests/test_python_connector.py @@ -3,6 +3,7 @@ import numpy as np import pytest +import sirf_simind_connection.connectors.python_connector as connector_mod from sirf_simind_connection.configs import get from sirf_simind_connection.connectors import RuntimeOperator, SimindPythonConnector from sirf_simind_connection.core.types import ScoringRoutine @@ -354,6 +355,85 @@ def test_python_connector_configure_voxel_phantom_accepts_scoring_routine_enum( assert int(connector.get_config().get_value(84)) == ScoringRoutine.PENETRATE.value +@pytest.mark.unit +def test_python_connector_configure_voxel_phantom_accepts_density_input_type( + tmp_path: Path, +): + connector = SimindPythonConnector( + config_source=get("AnyScan.yaml"), + output_dir=tmp_path, + output_prefix="case01", + ) + source = np.ones((3, 4, 5), dtype=np.float32) + density_g_cm3 = np.full_like(source, 1.5, dtype=np.float32) + + _, density_path = connector.configure_voxel_phantom( + source=source, + mu_map=density_g_cm3, + mu_map_type="density", + ) + + density_u16 = np.fromfile(density_path, dtype=np.uint16) + assert density_u16.size == density_g_cm3.size + assert np.all(density_u16 == 1500) + + +@pytest.mark.unit +def test_python_connector_configure_voxel_phantom_accepts_hu_input_type( + tmp_path: Path, monkeypatch +): + connector = SimindPythonConnector( + config_source=get("AnyScan.yaml"), + output_dir=tmp_path, + output_prefix="case01", + ) + source = np.ones((3, 4, 5), dtype=np.float32) + hu_map = np.full_like(source, 120.0, dtype=np.float32) + converted_density = np.full_like(source, 1.25, dtype=np.float32) + captured: dict[str, np.ndarray] = {} + + def fake_hu_to_density_schneider(values: np.ndarray) -> np.ndarray: + captured["input"] = values.copy() + return converted_density + + monkeypatch.setattr( + connector_mod, + "hu_to_density_schneider", + fake_hu_to_density_schneider, + ) + + _, density_path = connector.configure_voxel_phantom( + source=source, + mu_map=hu_map, + mu_map_type="hu", + ) + + density_u16 = np.fromfile(density_path, dtype=np.uint16) + assert density_u16.size == hu_map.size + assert np.all(density_u16 == 1250) + assert np.array_equal(captured["input"], hu_map) + + +@pytest.mark.unit +def test_python_connector_configure_voxel_phantom_rejects_unknown_mu_map_type( + tmp_path: Path, +): + connector = SimindPythonConnector( + config_source=get("AnyScan.yaml"), + output_dir=tmp_path, + output_prefix="case01", + ) + source = np.ones((3, 4, 5), dtype=np.float32) + mu_map = np.ones_like(source, dtype=np.float32) + + with pytest.raises(ValueError, match="mu_map_type must be one of"): + connector.configure_voxel_phantom( + source=source, + mu_map=mu_map, + mu_map_type="unsupported", + ) + + @pytest.mark.unit def test_python_connector_set_energy_windows_writes_window_file(tmp_path: Path): connector = SimindPythonConnector( diff --git a/tests/test_pytomography_adaptor.py b/tests/test_pytomography_adaptor.py index 627ee93..38d8960 100644 --- a/tests/test_pytomography_adaptor.py +++ b/tests/test_pytomography_adaptor.py @@ -84,6 +84,7 @@ def test_pytomography_adaptor_forwards_connector_wiring_and_axis_order( output_prefix="case01", voxel_size_mm=3.5, scoring_routine=ScoringRoutine.PENETRATE, + mu_map_type="hu", ) source_xyz = torch.arange(2 * 3 * 4, dtype=torch.float32).reshape(2, 3, 4) @@ -99,11 +100,13 @@ def fake_configure_voxel_phantom( mu_map, voxel_size_mm, scoring_routine, + mu_map_type, ): captured["source"] = np.asarray(source) captured["mu_map"] = np.asarray(mu_map) captured["voxel_size_mm"] = voxel_size_mm captured["scoring_routine"] = scoring_routine + captured["mu_map_type"] = mu_map_type return (tmp_path / "case01_src.smi", tmp_path / "case01_dns.dmi") def fake_set_energy_windows(lower_bounds, upper_bounds, scatter_orders): @@ -170,6 +173,7 @@ def fake_run(runtime_operator=None): assert np.array_equal(captured["mu_map"], expected_mu_zyx) assert captured["voxel_size_mm"] == pytest.approx(3.5) assert captured["scoring_routine"] == ScoringRoutine.PENETRATE + assert captured["mu_map_type"] == "hu" assert captured["windows"] == ([75.0, 120.0], [85.0, 140.0], [0, 1]) assert captured["runtime_operator"] is runtime_operator From 8ca69864a60faa593b00d5999e60d34ee9c39e41 Mon Sep 17 00:00:00 2001 From: Sam Porter <92305641+samdporter@users.noreply.github.com> Date: Wed, 11 Mar 2026 17:51:51 +0000 Subject: [PATCH 2/3] docs: document mu_map_type usage and changelog entry --- docs/changelog.rst | 11 +++++++++++ docs/usage.rst | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index f55d5c2..4366fbc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,11 +10,22 @@ The format is based on `Keep a Changelog ` Unreleased ---------- +Added +~~~~~ + +- ``mu_map_type`` input selection for SIMIND phantom setup, supporting + attenuation maps, density maps, and HU maps in + ``SimindPythonConnector.configure_voxel_phantom()``. + Changed ~~~~~~~ - Updated user-facing branding to ``py-smc`` and added explicit non-affiliation and external-SIMIND-install disclaimers in README/docs. +- STIR/SIRF/PyTomography adaptors now forward ``mu_map_type`` so input-map + interpretation is configurable across connector and adaptor workflows. +- Usage docs now describe ``mu_map_type`` options and example calls for + connector/adaptor setup. [0.5.0] - 2026-03-05 -------------------- diff --git a/docs/usage.rst b/docs/usage.rst index f5fbab0..101414e 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -42,6 +42,26 @@ inputs/outputs without any reconstruction-package dependency. total = outputs["tot_w1"].projection print(total.shape) +Map Input Types +--------------- + +``configure_voxel_phantom()`` supports three ``mu_map`` input conventions via +``mu_map_type``: + +- ``"attenuation"``: linear attenuation coefficients (cm^-1), converted to + density before SIMIND input writing. +- ``"density"``: density map in g/cm^3, passed through directly. +- ``"hu"``: CT HU map, converted to density with the Schneider model. + +.. code-block:: python + + connector.configure_voxel_phantom( + source=source, + mu_map=mu_map, + voxel_size_mm=4.0, + mu_map_type="attenuation", # or "density" / "hu" + ) + Adaptor Workflows ----------------- @@ -60,6 +80,7 @@ STIR adaptor config_source=get("Example.yaml"), output_dir="output/stir_adaptor", output_prefix="stir_case01", + mu_map_type="attenuation", # or "density" / "hu" ) adaptor.set_source(stir_source) adaptor.set_mu_map(stir_mu_map) @@ -83,6 +104,7 @@ SIRF adaptor config_source=get("Example.yaml"), output_dir="output/sirf_adaptor", output_prefix="sirf_case01", + mu_map_type="attenuation", # or "density" / "hu" ) adaptor.set_source(sirf_source) adaptor.set_mu_map(sirf_mu_map) @@ -113,6 +135,7 @@ Build the system matrix directly with PyTomography APIs. config_source=get("Example.yaml"), output_dir="output/pytomo_adaptor", output_prefix="pytomo_case01", + mu_map_type="attenuation", # or "density" / "hu" ) adaptor.set_source(source_tensor_xyz) adaptor.set_mu_map(mu_tensor_xyz) From 97d38352804ac7dafae7a960b83bdcd79e4034fd Mon Sep 17 00:00:00 2001 From: Sam Porter <92305641+samdporter@users.noreply.github.com> Date: Wed, 11 Mar 2026 17:56:54 +0000 Subject: [PATCH 3/3] chore: rename package branding to PySIMIND --- README.md | 4 ++-- docs/changelog.rst | 2 +- docs/conf.py | 6 +++--- docs/index.rst | 4 ++-- docs/intro.rst | 2 +- docs/testing.rst | 2 +- pyproject.toml | 2 +- sirf_simind_connection/__init__.py | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1e40429..210a47c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# py-smc +# PySIMIND Python SIMIND Monte Carlo Connector. @@ -41,7 +41,7 @@ install a licensed SIMIND installation. ### Basic Installation ```bash -pip install py-smc +pip install PySIMIND ``` Import path remains: diff --git a/docs/changelog.rst b/docs/changelog.rst index 4366fbc..cd371e8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,7 +20,7 @@ Added Changed ~~~~~~~ -- Updated user-facing branding to ``py-smc`` and added explicit non-affiliation +- Updated user-facing branding to ``PySIMIND`` and added explicit non-affiliation and external-SIMIND-install disclaimers in README/docs. - STIR/SIRF/PyTomography adaptors now forward ``mu_map_type`` so input-map interpretation is configurable across connector and adaptor workflows. diff --git a/docs/conf.py b/docs/conf.py index 76626b0..ea0111a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,4 @@ -"""Sphinx configuration file for py-smc documentation.""" +"""Sphinx configuration file for PySIMIND documentation.""" import os import sys @@ -10,11 +10,11 @@ sys.path.insert(0, os.path.abspath("..")) # Project information -project = "py-smc" +project = "PySIMIND" copyright = f"{datetime.now().year}, Sam Porter, Efstathios Varzakis" author = "Sam Porter, Efstathios Varzakis" release = "0.5.0" -for dist_name in ("py-smc", "sirf-simind-connection"): +for dist_name in ("PySIMIND", "py-smc", "sirf-simind-connection"): try: release = importlib_metadata.version(dist_name) break diff --git a/docs/index.rst b/docs/index.rst index ef2dd49..cef8af3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ -py-smc Documentation -==================== +PySIMIND Documentation +====================== .. toctree:: :maxdepth: 2 diff --git a/docs/intro.rst b/docs/intro.rst index a2c3fcc..611be0e 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -3,7 +3,7 @@ Introduction ============ -py-smc is a Python toolkit for SIMIND SPECT workflows. +PySIMIND is a Python toolkit for SIMIND SPECT workflows. Disclaimer ---------- diff --git a/docs/testing.rst b/docs/testing.rst index b953cdb..c5a69c4 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -3,7 +3,7 @@ Testing ======= -This document explains the testing strategy for py-smc, which handles the +This document explains the testing strategy for PySIMIND, which handles the challenge of testing code that depends on optional external dependencies (SIRF, STIR, SIMIND, and PyTomography) that may not be available in every environment. diff --git a/pyproject.toml b/pyproject.toml index 2b654fa..3441806 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "py-smc" +name = "PySIMIND" version = "0.5.0" description = "Python SIMIND Monte Carlo connector with STIR/SIRF/PyTomography adaptors" readme = "README.md" diff --git a/sirf_simind_connection/__init__.py b/sirf_simind_connection/__init__.py index 2f837e9..5df9e13 100644 --- a/sirf_simind_connection/__init__.py +++ b/sirf_simind_connection/__init__.py @@ -1,5 +1,5 @@ """ -py-smc connector/adaptor API. +PySIMIND connector/adaptor API. """ import importlib @@ -7,7 +7,7 @@ from typing import Any -for _dist_name in ("py-smc", "sirf-simind-connection", __name__): +for _dist_name in ("PySIMIND", "py-smc", "sirf-simind-connection", __name__): try: # installed (pip/poetry) __version__ = _meta.version(_dist_name) break