Changelog

All notable changes to this project are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

1.0.1 - 2026-04-16

Fixed

  • Corrected installation documentation to lead with the PyPI package install path instead of editable development installs.

  • Clarified that SIMIND is an external runtime dependency and must be available as simind on PATH for local connector runs.

  • Updated contributing and testing documentation to use Ruff for linting, import sorting, and formatting checks.

  • Updated examples documentation to include the DICOM-driven adaptor examples.

  • Fixed the GitHub Actions coverage target after the import package rename.

  • Fixed README links and release metadata for the patch release.

1.0.0 - 2026-04-16

Breaking Changes

Connector-first public API

The legacy simulator-oriented surface has been removed in favor of connector/adaptor entry points:

  • Removed SimindSimulator from top-level exports.

  • Removed NativeBackendConnector from connector exports.

  • Removed legacy core runtime modules (core.simulator, core.backend_adapter, core.components, core.file_managers, core.output_processor).

PyTomography adaptor boundary tightening

PyTomographySimindAdaptor no longer wraps reconstruction-package helper methods:

  • Removed build_system_matrix()

  • Removed get_pytomography_metadata()

System-matrix creation is now performed directly with PyTomography APIs in user code/examples.

Added

  • core.executor.SimindExecutor as a minimal subprocess runner for SIMIND.

  • Expanded unit coverage for connector/adaptor behavior: - native STIR/SIRF adaptor validation and forwarding behavior - PyTomography adaptor connector wiring and axis-order forwarding - additional configure_voxel_phantom edge cases in Python connector tests

  • Geometry documentation note for STIR/SIRF voxel-size extraction fallback behavior.

Changed

  • Renamed the PyPI distribution to simind-python-connector.

  • Renamed the import package to simind_python_connector.

  • Updated user-facing branding to simind-python-connector and added explicit non-affiliation and external-SIMIND-install disclaimers in README/docs.

  • SimindPythonConnector is now the central execution path for adaptor workflows.

  • StirSimindAdaptor and SirfSimindAdaptor now delegate SIMIND setup/run through SimindPythonConnector and return native backend projection types.

  • PyTomographySimindAdaptor remains focused on SIMIND I/O adaptation and tensor conversion only.

  • README and docs pages were rewritten with user-facing guidance for connector and adaptor workflows.

Fixed

  • STIR/SIRF voxel-size extraction fallback now supports both 4-element and 3-element get_grid_spacing() outputs, plus non-iterable coordinate objects (for example, STIR Float3BasicCoordinate) for z-spacing extraction.

  • SimindExecutor command invocation hardened with explicit token validation and shell=False execution.

[0.4.0] - 2026-02-26

Breaking Changes

Example suite renumbering and adaptor split

The old monolithic OSEM examples were replaced with backend-specific adaptor examples:

  • 07A_stir_adaptor_osem.py

  • 07B_sirf_adaptor_osem.py

  • 07C_pytomography_adaptor_osem.py

  • 08A/08B/08C DICOM-driven adaptor examples

Legacy numbering and naming references should be updated to the new files.

Connector/adaptor naming normalization

Public docs and examples now consistently use:

  • SimindPythonConnector for backend-agnostic NumPy workflows

  • StirSimindAdaptor / SirfSimindAdaptor / PyTomographySimindAdaptor for backend-native workflows

Added

  • Geometry documentation for SIMIND/STIR/SIRF/PyTomography axis conventions (docs/geometry.rst).

  • Container scripts for split validation and examples: scripts/run_container_validation.sh and scripts/run_container_examples.sh.

  • Backend-isolation and connector/adaptor unit tests (markers + import gating).

  • Example profile config (configs/Example.yaml) and Python-connector helper flow for lightweight deterministic runs.

Changed

  • Core examples 01-06 now use the pure Python connector path.

  • OSEM examples now use dedicated adaptor paths and backend-specific outputs.

  • Documentation updated for new example numbering, runtime flags, and testing entry points.

  • Versioning aligned across package metadata and docs.

Fixed

  • PENETRATE component header parsing via .bXX outputs in the Python connector.

  • SIMIND optional-availability handling in container scripts (fail-fast option plus skip-by-default behavior when binary is absent).

  • Quantization scaling docs and naming consistency (quantization_scale).

[0.3.0] - 2025-01-23

Breaking Changes

Backend-Agnostic Public API

All public methods now return wrapped backend-agnostic objects instead of native SIRF/STIR objects:

  • SimindSimulator.get_total_output() → Returns AcquisitionDataInterface

  • SimindSimulator.get_scatter_output() → Returns AcquisitionDataInterface

  • SimindSimulator.get_primary_output() → Returns AcquisitionDataInterface

  • SimindSimulator.get_penetrate_output() → Returns AcquisitionDataInterface

  • SimindToStirConverter.convert(return_object=True) → Returns AcquisitionDataInterface

  • convert_simind_to_stir() → Returns AcquisitionDataInterface

Migration: Use .native_object property to access underlying SIRF/STIR objects if needed for backend-specific operations. Wrapper objects support common operations (.write(), .sum(), .as_array(), etc.).

Backend-Agnostic extract_attributes_from_stir()

Function now only accepts filepath (string), not acquisition data objects:

  • Before: extract_attributes_from_stir(acq_obj) or extract_attributes_from_stir(filepath)

  • After: extract_attributes_from_stir(filepath) only

  • Migration: If you have an acquisition object, write it to a file first: acq_obj.write(temp_path); extract_attributes_from_stir(temp_path)

Changed

Enhanced Factory Functions

Factory functions now accept multiple input types:

  • String filepath → Loads and wraps

  • Already wrapped object → Returns as-is (idempotent)

  • Native SIRF/STIR object → Wraps it

  • None → Creates empty object (SIRF only)

Internal Template Storage

SimindSimulator now stores both template filepath (backend-agnostic) and wrapped object for efficient operations.

Fixed

Critical: Coordinator Update Timing Fix

Fixed off-by-one error in coordinator update scheduling:

  • Issue: Coordinator updates were occurring at iteration N, but callbacks (UpdateEtaCallback, ArmijoTriggerCallback) ran AFTER the step was taken, causing Armijo line search to run one iteration late with a mismatched objective function

  • Fix: Modified SimindCoordinator.should_update() and StirPsfCoordinator.should_update() to trigger updates ONE iteration early (at correction_update_interval - 1 instead of correction_update_interval)

  • Impact: Ensures proper callback ordering for convergent optimization

  • Result: Eliminates divergence caused by taking steps with wrong objective, ensures convexity is maintained

Added

Backend Abstraction Layer

New backend system supporting both SIRF and STIR Python:

  • Factory functions: create_image_data(), create_acquisition_data()

  • Utility functions: get_backend(), set_backend(), is_sirf_backend()

  • Wrapper classes: SirfImageData, SirfAcquisitionData, StirImageData, StirAcquisitionData

  • See Backend and Adaptor Dependencies for complete documentation

Utility Functions for STIR Compatibility
  • to_projdata_in_memory(): Convert ProjData to ProjDataInMemory for arithmetic operations (STIR Python compatibility)

  • Fixes arithmetic operations (+, -, *) on STIR ProjData objects

  • Works transparently with both SIRF and STIR backends

Example Updates
  • Updated examples 01-06 to support both SIRF and STIR Python backends

  • Added --backend command-line option to force specific backend

  • Fixed arithmetic operations in example 03 (TEW correction)

ShiftedKullbackLeibler Support
  • Added conditional support for SETR’s ShiftedKullbackLeibler in data partitioning

  • New use_shifted_kl parameter in partition_data_with_cil_objectives() function

  • Automatic fallback to standard KullbackLeibler when SETR is not available

ResidualCorrectedKullbackLeibler Jordan Split
  • Residual-aware KL now decomposes residuals into positive/negative parts

  • Keeps the objective convex and allows arbitrary residual signs

Changed

Documentation Organization
  • Moved BACKENDS.md to docs/backends.rst (reStructuredText format)

  • Updated docs/changelog.rst with comprehensive change history

  • Added backends page to documentation index

Version 0.2.1

New Features

Schneider2000 Density Conversion

Advanced 44-segment piecewise model for HU-to-density conversion:

  • hu_to_density_schneider(): Interpolated conversion using all 44 tissue segments

  • hu_to_density_schneider_piecewise(): Exact piecewise conversion matching lookup table

  • get_schneider_tissue_info(): Lookup tissue information for specific HU values

  • compare_density_methods(): Compare bilinear vs Schneider methods

  • Enhanced Accuracy: ~0.17-0.19 g/cm³ improved accuracy over bilinear model

  • Comprehensive Tissue Support: Covers air, lung variations, soft tissues, bones, and metal implants

  • New Example: 06_schneider_density_conversion.py demonstrates advanced density conversion

Coordinator Architecture

Efficient subset reconstruction with shared Monte Carlo corrections:

  • SimindCoordinator: Manages single SIMIND simulation for all subsets

  • SimindSubsetProjector: Projector for individual subsets

  • CIL partitioner utilities for subset-based reconstruction

  • 12× faster for 12-subset reconstruction

  • See coordinator-related API/docs sections for implementation details

SimindProjector AcquisitionModel Interface

Drop-in replacement for SIRF AcquisitionModel with Monte Carlo corrections:

  • Complete AcquisitionModel API compatibility

  • Three correction modes (residual only, additive only, both)

  • Automatic iteration tracking

  • Intelligent scaling strategy

  • CIL framework compatibility

Improvements

  • Extended documentation with density conversion methods comparison

  • Comprehensive test suite for Schneider functionality (16 new tests)

  • Enhanced attenuation conversion utilities with clinical-grade accuracy

  • MPI parallel execution support for SIMIND simulations

  • STIR CUDARelativeDifferencePrior integration

Fixed

Critical: Non-Circular Orbit Support

Multiple fixes for proper orbit file handling:

  • Issue 1: Missing orbit file creation (AcquisitionData.get_info() doesn’t include orbit/radii)

  • Issue 2: Missing unit conversion (radii in mm instead of cm)

  • Issue 3: File naming collision (input orbit file overwritten)

  • Issue 4: Incorrect command argument order

  • Issue 5: Full path instead of filename

Critical: Subset View Index Out of Range
  • Fixed coordinator receiving subset acquisition models but attempting to extract using full-data indices

  • Coordinator now requires full-data acquisition models passed to __init__()

Module Import Issues
  • Fixed RecursionError in lazy imports (converters, utils, builders modules)

  • Wrapped SIRF imports in try/except blocks for better fallback behavior

Version 0.2.0

Breaking Changes

  • Modified config file loading mechanism in SimulationConfig class

  • Updated API for configuration initialization

New Features

  • Comprehensive test suite with unit and integration tests

  • Enhanced documentation with ReadTheDocs support

  • GitHub Actions CI/CD pipeline

  • Auto-generated API documentation

  • Professional documentation structure

Improvements

  • Better test coverage for all components

  • Improved code quality with automated checks

  • Streamlined README for better user experience

Version 0.1.1

  • Bug fixes and minor improvements from initial release

Version 0.1.0

  • Initial release with core functionalities and examples