Metadata-Version: 2.4
Name: simind-python-connector
Version: 3.0.0
Summary: Python SIMIND Monte Carlo connector with STIR/SIRF/PyTomography adaptors
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>
Maintainer-email: Sam Porter <sam.porter.18@ucl.ac.uk>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/samdporter/simind-python-connector
Project-URL: Documentation, https://simind-python-connector.readthedocs.io/
Project-URL: Repository, https://github.com/samdporter/simind-python-connector
Project-URL: Issues, https://github.com/samdporter/simind-python-connector/issues
Project-URL: Changelog, https://github.com/samdporter/simind-python-connector/blob/main/CHANGELOG.md
Keywords: SPECT,Monte Carlo,medical imaging,SIMIND,SIRF,STIR
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.20.0
Requires-Dist: pydicom>=2.0.0
Requires-Dist: pyyaml>=5.4.0
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: pytest-mock; extra == "dev"
Requires-Dist: scipy; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: sphinx; extra == "dev"
Requires-Dist: sphinx-rtd-theme; extra == "dev"
Requires-Dist: sphinx-autodoc-typehints; extra == "dev"
Requires-Dist: myst-parser; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Provides-Extra: examples
Requires-Dist: matplotlib>=3.0.0; extra == "examples"
Provides-Extra: recon
Requires-Dist: scipy; extra == "recon"
Dynamic: license-file

# simind-python-connector

Python SIMIND Monte Carlo Connector.

[![Tests](https://github.com/samdporter/simind-python-connector/workflows/Tests/badge.svg)](https://github.com/samdporter/simind-python-connector/actions)
[![Documentation Status](https://readthedocs.org/projects/simind-python-connector/badge/?version=latest)](https://simind-python-connector.readthedocs.io/en/latest/?badge=latest)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)

A Python toolkit that lets you run SIMIND from Python and use the outputs in
common reconstruction ecosystems (STIR, SIRF, PyTomography).

## Disclaimer

This project is an independent Python connector toolkit and is **not
affiliated with, endorsed by, or maintained by** the SIMIND project or Lund
University.

SIMIND is **not distributed** with this package. You must separately obtain and
install a licensed SIMIND installation.

## Quick Links
- [Full Documentation](https://simind-python-connector.readthedocs.io/)
- [Installation](https://simind-python-connector.readthedocs.io/en/latest/installation.html)
- [Backend Support](https://simind-python-connector.readthedocs.io/en/latest/backends.html) - adaptor dependency matrix
- [Reconstruction with SIMIND corrections](https://simind-python-connector.readthedocs.io/en/latest/reconstruction.html)

## What This Package Does
1. Runs SIMIND from Python with a minimal, explicit API.
2. Adapts SIMIND data for widely used Python reconstruction packages.

## Key Features
- **Connector-first API** - `SimindPythonConnector` for direct SIMIND execution from Python
- **Package Adaptors** - STIR/SIRF/PyTomography adaptors for reconstruction workflows
- **Native reconstruction workflows** - Use STIR/SIRF/PyTomography reconstruction tools with generated SIMIND data
- **Reconstruction with SIMIND corrections (optional)** - `simind_python_connector.recon`: Monte Carlo scatter and Fu & Qi residual correction in SIRF OSEM and CIL algorithms
- **Dual scoring support** - SCATTWIN and PENETRATE
- **DICOM builders** - DICOM-driven setup utilities for scanner/input preparation
- **Advanced Schneider2000 density conversion** - 44-segment HU-to-density mapping

## Installation

### Basic Installation

```bash
pip install simind-python-connector
```

Import with:

```python
import simind_python_connector
```

For example plotting dependencies:

```bash
pip install "simind-python-connector[examples]"
```

### Adaptor Dependencies

`SimindPythonConnector` works without SIRF/STIR/PyTomography.

Install optional packages only for the adaptor paths you need:

- **STIR Python** for `StirSimindAdaptor` workflows (example 07A)
- **SIRF** for `SirfSimindAdaptor` workflows (example 07B)
- **PyTomography** for `PyTomographySimindAdaptor` workflows (example 07C)

#### Option 1: STIR Python (Recommended for basic usage)

Install from conda:
```bash
conda install -c conda-forge stir "numpy<2.0"
```

**Note**: If STIR from conda-forge fails with NumPy binary-compatibility errors, install `numpy<2`.

Or build from source:
```bash
git clone https://github.com/UCL/STIR.git
cd STIR
# Follow build instructions in the repository
```

#### Option 2: SIRF

Install from source:
```bash
git clone https://github.com/SyneRBI/SIRF.git
cd SIRF
# Follow build instructions in the repository
```

**Note**: SIRF includes STIR, so a separate STIR install is usually unnecessary.

#### Option 3: PyTomography

Install PyTomography for the PyTomography adaptor workflow:

```bash
pip install pytomography
```

### SIMIND Requirement

SIMIND is **not included** with this package and must be installed separately.

Use the official SIMIND resources:

- SIMIND site (Medical Radiation Physics, Lund University): https://www.msf.lu.se/en/research/simind-monte-carlo-program
- SIMIND manual/docs: https://www.msf.lu.se/en/research/simind-monte-carlo-program/manual

For local use with this package's scripts, place your SIMIND installation under:

```text
./simind
```

and make sure the executable is available at:

```text
./simind/simind
```

and ensure SIMIND data files are available under:

```text
./simind/smc_dir/
```

The Docker helper scripts use this layout and automatically wire SIMIND paths
when the binary exists at `./simind/simind`.

For direct local runs outside the repository helpers, make sure the SIMIND
executable is available as `simind` on `PATH`.

## Quick Start
```python
import numpy as np
from simind_python_connector import SimindPythonConnector
from simind_python_connector.configs import get

source = np.zeros((32, 32, 32), dtype=np.float32)  # z, y, x
source[12:20, 12:20, 12:20] = 1.0
mu_map = np.zeros_like(source)
mu_map[source > 0] = 0.15

connector = SimindPythonConnector(
    config_source=get("Example.yaml"),
    output_dir="output/basic",
    output_prefix="case01",
    quantization_scale=0.05,
)

connector.configure_voxel_phantom(
    source=source,
    mu_map=mu_map,
    voxel_size_mm=4.0,
)
connector.set_energy_windows([126], [154], [0])  # Tc-99m ± 10%
connector.add_runtime_switch("FI", "tc99m")
connector.add_runtime_switch("CC", "ma-lehr")
connector.add_runtime_switch("NN", 1)
connector.add_runtime_switch("RR", 12345)

outputs = connector.run()
total = outputs["tot_w1"].projection
print(total.shape)
```

### Advanced Density Conversion

```python
from simind_python_connector.converters.attenuation import hu_to_density_schneider
import numpy as np

# Convert HU image to densities using Schneider2000 model
hu_image = np.array([[-1000, 0, 500], [800, 1200, 2000]])
density_map = hu_to_density_schneider(hu_image)  # 44-segment clinical model
```

### Pure Python Connector (NumPy Outputs)

```python
from simind_python_connector import RuntimeOperator, SimindPythonConnector
from simind_python_connector.configs import get

connector = SimindPythonConnector(
    config_source=get("AnyScan.yaml"),
    output_dir="output/python_connector",
    output_prefix="case01",
    quantization_scale=1.0,
)

outputs = connector.run(RuntimeOperator(switches={"NN": 1, "RR": 12345}))
total = outputs["tot_w1"]
print(total.projection.shape)
print(total.header_path)
```

For minimal toy runs with smaller projection/image settings, use
`get("Example.yaml")`.

`quantization_scale` controls source integer quantization before writing SIMIND
`.smi` files:

- `1.0`: uses full internal source integer range (best numeric precision)
- `< 1.0`: reduces integer scale for faster toy runs but increases rounding error

SIMIND treats source maps as integer weights; absolute activity/time scaling is
controlled separately by simulation settings.

## Docker Environments

Dedicated container definitions are provided in [`docker/`](docker/):

- `docker/stir/Dockerfile`
- `docker/sirf/Dockerfile`
- `docker/pytomography/Dockerfile`

Use Docker Compose:

```bash
docker compose -f docker/compose.yaml build stir
docker compose -f docker/compose.yaml run --rm stir
```

Run per-backend validation and separated examples:

```bash
bash scripts/run_container_validation.sh
bash scripts/run_container_validation.sh --with-simind
bash scripts/run_container_examples.sh
```

## Contributing
Please see the [Contributing Guide](docs/contributing.rst).

## License
Apache License 2.0
