multimodars 0.3.2__tar.gz → 0.3.4__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 (123) hide show
  1. {multimodars-0.3.2 → multimodars-0.3.4}/CHANGELOG.md +16 -4
  2. multimodars-0.3.4/CITATION.cff +82 -0
  3. {multimodars-0.3.2 → multimodars-0.3.4}/Cargo.lock +1 -1
  4. {multimodars-0.3.2 → multimodars-0.3.4}/Cargo.toml +1 -1
  5. {multimodars-0.3.2 → multimodars-0.3.4}/PKG-INFO +3 -3
  6. {multimodars-0.3.2 → multimodars-0.3.4}/README.md +2 -2
  7. multimodars-0.3.4/ROADMAP.MD +43 -0
  8. {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/BENCHMARK_PLAN.md +8 -8
  9. {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/benchmark_bruteforce_stepsize.py +10 -10
  10. {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/benchmark_cpu_scaling.py +7 -6
  11. multimodars-0.3.4/benchmarks/results/bruteforce_stepsize.png +0 -0
  12. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/_processing.py +42 -42
  13. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/multimodars.pyi +6 -6
  14. multimodars-0.3.4/paper/REVIEWER2.md +48 -0
  15. {multimodars-0.3.2 → multimodars-0.3.4}/paper/bibliography.bib +48 -12
  16. multimodars-0.3.4/paper/paper.md +126 -0
  17. {multimodars-0.3.2 → multimodars-0.3.4}/pyproject.toml +1 -0
  18. {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/adjust_mesh/label_coronary.rs +2 -2
  19. multimodars-0.3.4/src/intravascular/binding/align.rs +397 -0
  20. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/centerline_align/align_algorithms.rs +75 -62
  21. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/centerline_align/mod.rs +132 -87
  22. multimodars-0.3.4/src/intravascular/to_object/mod.rs +130 -0
  23. multimodars-0.3.4/tests/test_intravascular.py +256 -0
  24. multimodars-0.3.2/ROADMAP.MD +0 -93
  25. multimodars-0.3.2/benchmarks/results/bruteforce_stepsize.png +0 -0
  26. multimodars-0.3.2/paper/paper.md +0 -128
  27. multimodars-0.3.2/src/intravascular/binding/align.rs +0 -335
  28. multimodars-0.3.2/src/intravascular/to_object/mod.rs +0 -66
  29. {multimodars-0.3.2 → multimodars-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  30. {multimodars-0.3.2 → multimodars-0.3.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  31. {multimodars-0.3.2 → multimodars-0.3.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  32. {multimodars-0.3.2 → multimodars-0.3.4}/.github/workflows/CI.yml +0 -0
  33. {multimodars-0.3.2 → multimodars-0.3.4}/.github/workflows/draft-pdf.yml +0 -0
  34. {multimodars-0.3.2 → multimodars-0.3.4}/.gitignore +0 -0
  35. {multimodars-0.3.2 → multimodars-0.3.4}/.pre-commit-config.yaml +0 -0
  36. {multimodars-0.3.2 → multimodars-0.3.4}/.readthedocs.yaml +0 -0
  37. {multimodars-0.3.2 → multimodars-0.3.4}/CONTRIBUTING.md +0 -0
  38. {multimodars-0.3.2 → multimodars-0.3.4}/LICENSE +0 -0
  39. {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/results/cpu_scaling.png +0 -0
  40. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/branch_diastolic_contours.csv +0 -0
  41. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/branch_systolic_contours.csv +0 -0
  42. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/calcium_diastolic_contours.csv +0 -0
  43. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/calcium_systolic_contours.csv +0 -0
  44. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/diastolic_contours.csv +0 -0
  45. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/diastolic_reference_points.csv +0 -0
  46. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/eem_diastolic_contours.csv +0 -0
  47. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/eem_systolic_contours.csv +0 -0
  48. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/systolic_contours.csv +0 -0
  49. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/systolic_reference_points.csv +0 -0
  50. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/branch_diastolic_contours.csv +0 -0
  51. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/branch_systolic_contours.csv +0 -0
  52. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/calcium_diastolic_contours.csv +0 -0
  53. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/calcium_systolic_contours.csv +0 -0
  54. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/diastolic_contours.csv +0 -0
  55. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/diastolic_reference_points.csv +0 -0
  56. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/eem_diastolic_contours.csv +0 -0
  57. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/eem_systolic_contours.csv +0 -0
  58. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/systolic_contours.csv +0 -0
  59. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/systolic_reference_points.csv +0 -0
  60. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/combined_sorted_manual.csv +0 -0
  61. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/diastolic_contours.csv +0 -0
  62. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/diastolic_reference_points.csv +0 -0
  63. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/systolic_contours.csv +0 -0
  64. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/systolic_reference_points.csv +0 -0
  65. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/combined_sorted_manual.csv +0 -0
  66. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/diastolic_contours.csv +0 -0
  67. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/diastolic_reference_points.csv +0 -0
  68. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/systolic_contours.csv +0 -0
  69. {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/systolic_reference_points.csv +0 -0
  70. {multimodars-0.3.2 → multimodars-0.3.4}/media/coronary_artery_disease.jpg +0 -0
  71. {multimodars-0.3.2 → multimodars-0.3.4}/media/dynamic_lumen_changes.png +0 -0
  72. {multimodars-0.3.2 → multimodars-0.3.4}/media/multimoda-rs.jpg +0 -0
  73. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/__init__.py +0 -0
  74. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/_converters.py +0 -0
  75. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/__init__.py +0 -0
  76. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/debug_plots.py +0 -0
  77. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/fixing_functions.py +0 -0
  78. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/labeling.py +0 -0
  79. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/manipulating.py +0 -0
  80. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/io/__init__.py +0 -0
  81. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/io/read_geometrical.py +0 -0
  82. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/io/write_geometries.py +0 -0
  83. {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/py.typed +0 -0
  84. {multimodars-0.3.2 → multimodars-0.3.4}/paper/REVIEWER1.md +0 -0
  85. {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure1.jpg +0 -0
  86. {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure2.jpg +0 -0
  87. {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure3.png +0 -0
  88. {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure4.jpg +0 -0
  89. {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/adjust_mesh/mod.rs +0 -0
  90. {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/adjust_mesh/scale_coronary.rs +0 -0
  91. {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/binding/ccta_py.rs +0 -0
  92. {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/binding/mod.rs +0 -0
  93. {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/mod.rs +0 -0
  94. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/binding/classes.rs +0 -0
  95. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/binding/entry.rs +0 -0
  96. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/binding/mod.rs +0 -0
  97. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/centerline_align/preprocessing.rs +0 -0
  98. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/geometry.rs +0 -0
  99. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/input.rs +0 -0
  100. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/integrity_check.rs +0 -0
  101. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/mod.rs +0 -0
  102. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/output.rs +0 -0
  103. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/mod.rs +0 -0
  104. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/align_between.rs +0 -0
  105. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/align_within.rs +0 -0
  106. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/mod.rs +0 -0
  107. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/postprocessing.rs +0 -0
  108. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/preprocessing.rs +0 -0
  109. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/process_utils.rs +0 -0
  110. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/wall.rs +0 -0
  111. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/to_object/interpolation.rs +0 -0
  112. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/to_object/texture.rs +0 -0
  113. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/to_object/write_mtl.rs +0 -0
  114. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/utils/general_utils.rs +0 -0
  115. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/utils/mod.rs +0 -0
  116. {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/utils/test_utils.rs +0 -0
  117. {multimodars-0.3.2 → multimodars-0.3.4}/src/lib.rs +0 -0
  118. {multimodars-0.3.2 → multimodars-0.3.4}/tests/conftest.py +0 -0
  119. {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_ccta.py +0 -0
  120. {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_converters.py +0 -0
  121. {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_core.py +0 -0
  122. {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_wrappers.py +0 -0
  123. {multimodars-0.3.2 → multimodars-0.3.4}/tests/tests_todo.md +0 -0
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.3.4] - 2026-05-13
7
+
8
+ ### Changed
9
+ - `align_three_point`, `align_manual` and `align_combined` are now all generic, taking either `PyGeometryPair` or `PyGeometry` as input
10
+ - Test file added `test_intravascular.py` to demonstrate that same results with both
11
+
12
+ ## [0.3.3] - 2026-05-06
13
+
14
+ ### Added
15
+ - `CITATION.cff` with full author list, ORCIDs, and preferred citation for JOSS/Zenodo archival.
16
+ - Zenodo DOI badge added to README.
17
+
6
18
  ## [0.3.2] - 2026-05-05
7
19
 
8
20
  ### Added
@@ -14,14 +26,14 @@ This project adheres to [Semantic Versioning](https://semver.org/).
14
26
 
15
27
  ### Changed
16
28
  - `paper/paper.md` Performance section updated with quantified benchmark results and
17
- corrected parallelisation description.
29
+ corrected parallelization description.
18
30
 
19
31
  ### Performance
20
- - Intra-pullback alignment (`search_range`) now parallelises over candidate rotation
32
+ - Intra-pullback alignment (`search_range`) now parallelizes over candidate rotation
21
33
  angles via `par_iter` instead of the point-rotation loop. The inner cost-function
22
34
  loop is now sequential. This provides enough rayon tasks per frame step to saturate
23
- all cores: bruteforce scales 6.5× and optimised 4.2× from 2 to 16 cores (previously
24
- ~1.4× for both). Combined algorithmic + parallelisation gain reaches **38.5×** vs.
35
+ all cores: bruteforce scales 6.5× and optimized 4.2× from 2 to 16 cores (previously
36
+ ~1.4× for both). Combined algorithmic + parallelization gain reaches **38.5×** vs.
25
37
  brute-force at 2 cores.
26
38
 
27
39
  ## [0.3.1] - 2026-05-04
@@ -0,0 +1,82 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it using the metadata below."
3
+ title: "multimodars: A Rust-powered toolkit for multi-modality cardiac image fusion and registration"
4
+ version: 0.3.2
5
+ date-released: 2026-05-05
6
+ license: MIT
7
+ repository-code: "https://github.com/yungselm/multimoda-rs"
8
+ url: "https://multimoda-rs.readthedocs.io"
9
+
10
+ authors:
11
+ - family-names: Stark
12
+ given-names: Anselm W.
13
+ orcid: "https://orcid.org/0000-0002-5861-3753"
14
+ affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
15
+ - family-names: Ilic
16
+ given-names: Marc
17
+ orcid: "https://orcid.org/0009-0000-6443-8739"
18
+ affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
19
+ - family-names: Mokhtari
20
+ given-names: Ali
21
+ orcid: "https://orcid.org/0009-0004-8864-6988"
22
+ affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
23
+ - family-names: Mohammadi Kazaj
24
+ given-names: Pooya
25
+ orcid: "https://orcid.org/0000-0003-2747-1489"
26
+ affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
27
+ - family-names: Gräni
28
+ given-names: Christoph
29
+ orcid: "https://orcid.org/0000-0001-6029-0597"
30
+ affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
31
+ - family-names: Shiri
32
+ given-names: Isaac
33
+ orcid: "https://orcid.org/0000-0002-5735-0736"
34
+ affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
35
+
36
+ abstract: >
37
+ multimodars is a high-performance Python/Rust toolkit that registers and fuses
38
+ intravascular imaging (IVUS, OCT) with coronary CT angiography (CCTA) into unified
39
+ high-resolution 3D vessel models. It implements four alignment modes (full,
40
+ double-pair, single-pair, single) to support multi-state comparisons such as rest
41
+ vs. pharmacologic stress and pre- vs. post-intervention. The Rust backend (via
42
+ PyO3/maturin) parallelizes the multiscale angular search across all available cores,
43
+ achieving a combined 38.5x speedup over a Python brute-force baseline. Output
44
+ includes Wavefront OBJ/STL meshes, per-frame lumen metrics (minimum lumen area,
45
+ stenosis fraction, elliptic ratio), and optional interpolated deformation animations.
46
+
47
+ keywords:
48
+ - Rust
49
+ - Python
50
+ - Cardiology
51
+ - Intravascular imaging
52
+ - CCTA
53
+ - Image Fusion
54
+ - Image registration
55
+ - IVUS
56
+ - OCT
57
+ - Coronary artery anomalies
58
+
59
+ preferred-citation:
60
+ type: article
61
+ title: "multimodars: A Rust-powered toolkit for multi-modality cardiac image fusion and registration"
62
+ authors:
63
+ - family-names: Stark
64
+ given-names: Anselm W.
65
+ orcid: "https://orcid.org/0000-0002-5861-3753"
66
+ - family-names: Ilic
67
+ given-names: Marc
68
+ orcid: "https://orcid.org/0009-0000-6443-8739"
69
+ - family-names: Mokhtari
70
+ given-names: Ali
71
+ orcid: "https://orcid.org/0009-0004-8864-6988"
72
+ - family-names: Mohammadi Kazaj
73
+ given-names: Pooya
74
+ orcid: "https://orcid.org/0000-0003-2747-1489"
75
+ - family-names: Gräni
76
+ given-names: Christoph
77
+ orcid: "https://orcid.org/0000-0001-6029-0597"
78
+ - family-names: Shiri
79
+ given-names: Isaac
80
+ orcid: "https://orcid.org/0000-0002-5735-0736"
81
+ journal: "Journal of Open Source Software"
82
+ year: 2026
@@ -753,7 +753,7 @@ dependencies = [
753
753
 
754
754
  [[package]]
755
755
  name = "multimodars"
756
- version = "0.3.2"
756
+ version = "0.3.4"
757
757
  dependencies = [
758
758
  "anyhow",
759
759
  "approx",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "multimodars"
3
- version = "0.3.2"
3
+ version = "0.3.4"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multimodars
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -36,7 +36,7 @@ Project-URL: Issues, https://github.com/yungselm/multimoda-rs/issues
36
36
  [![Docs](https://img.shields.io/readthedocs/multimoda-rs)](https://multimoda-rs.readthedocs.io)
37
37
  [![Tests and Build](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml/badge.svg)](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml)
38
38
  [![status](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80/status.svg)](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80)
39
-
39
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20051997.svg)](https://doi.org/10.5281/zenodo.20051997)
40
40
 
41
41
  <figure class="epigraph" style="text-align: center; font-style: italic;">
42
42
  <blockquote>
@@ -79,7 +79,7 @@ For building from source or the full developer setup (tests, linting, docs), see
79
79
 
80
80
  ## Quick Example
81
81
 
82
- Checkout our **[Documentation](https://multimod-rs.readthedocs.io/en/latest)** for in-depth explanations of the functionality and preloaded notebooks including 3D plots. Otherwise clone the repo to follow along the different examples:
82
+ Checkout our **[Documentation](https://multimoda-rs.readthedocs.io/en/latest)** for in-depth explanations of the functionality and preloaded notebooks including 3D plots. Otherwise clone the repo to follow along the different examples:
83
83
 
84
84
  ```python
85
85
  import multimodars as mm
@@ -12,7 +12,7 @@
12
12
  [![Docs](https://img.shields.io/readthedocs/multimoda-rs)](https://multimoda-rs.readthedocs.io)
13
13
  [![Tests and Build](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml/badge.svg)](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml)
14
14
  [![status](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80/status.svg)](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80)
15
-
15
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20051997.svg)](https://doi.org/10.5281/zenodo.20051997)
16
16
 
17
17
  <figure class="epigraph" style="text-align: center; font-style: italic;">
18
18
  <blockquote>
@@ -55,7 +55,7 @@ For building from source or the full developer setup (tests, linting, docs), see
55
55
 
56
56
  ## Quick Example
57
57
 
58
- Checkout our **[Documentation](https://multimod-rs.readthedocs.io/en/latest)** for in-depth explanations of the functionality and preloaded notebooks including 3D plots. Otherwise clone the repo to follow along the different examples:
58
+ Checkout our **[Documentation](https://multimoda-rs.readthedocs.io/en/latest)** for in-depth explanations of the functionality and preloaded notebooks including 3D plots. Otherwise clone the repo to follow along the different examples:
59
59
 
60
60
  ```python
61
61
  import multimodars as mm
@@ -0,0 +1,43 @@
1
+ # Python package that has one purpose: Combining any intravascular image modality with CCTA meshes.
2
+
3
+ # Hot fixes
4
+ - [] Labeling doesn't work as expected especially for `from_file` variants
5
+
6
+ # Optimizations
7
+ - [] Optimize the ray intersection algorithm
8
+ - [] Better parallelization strategy for frames alignment by processing in batches (frames only have to be aligned relative to each other not sequentially)
9
+
10
+ # Mesh combination (v0.3.0)
11
+ ## New/improved features
12
+ - [] "Smooth" the aortic ring of boundary points first, by projecting on plane to shift outliers
13
+ - [] Write a more stable function to find intramural points on aorta (for now works with excluded RCA points)
14
+ - [] Create an aortic and coronary wall with a consistent connection
15
+ - [] Find bifurcation reference points automatically
16
+ - [] `numpy_to_report` function that allows to read in lists for the four variables of interest (frame, phase, measurement_1, measurement_2)
17
+ - [] write metadata to .obj files so they can be reconstructed completely to geometry/geometrypair
18
+
19
+ ## Fixes/Improvements
20
+ - [] Write a more stable function to find intramural points on aorta (for now works with excluded RCA points)
21
+ - [] Align algorithms only work with geometry pair not only geometry
22
+ - [] fix centerline resampling
23
+
24
+ ## Investigate/check logic
25
+ - [] When aligning geometry with wall has a rotation in the wall -> logic error for centerline alignment?
26
+
27
+ # CCTA clinical assessment (v0.4.0)
28
+ - [] automatically find the required reference points for anomalous alignment
29
+ - [] automatically assess lumen area, minor-, major axis, mla, intramural length etc. from the CCTA mesh.
30
+ - [] return hausdorff distances per point between aligned ivus mesh and ccta mesh.
31
+ - [] function to automatically assign wall thickness to ccta and combined ivus/ccta mesh
32
+
33
+ # Miscellaneous
34
+ - [] Integrate coverage measurement (`pytest-cov`) and upload reports to Coveralls or Codecov.
35
+ - [] Add a coverage status badge to the README.
36
+
37
+ # Centerline alignment module (v1.0.0)
38
+ - [] Create Centerline directly from mesh
39
+
40
+ # Myocardial Fusion v2.0.0
41
+ - [] Read in LGE/T2 etc. from MRI and map onto CCTA myocardium
42
+ - [] Read in Perfusion map from PET/SPECT and load onto CCTA myocardium
43
+ - [] Calculate perfusion territories based on coronary anatomy
@@ -6,18 +6,18 @@ Two benchmarks to characterise the alignment performance of `multimodars`.
6
6
 
7
7
  | # | What | Function | Dataset |
8
8
  |---|------|----------|---------|
9
- | 1 | Bruteforce vs. optimised at varying step sizes | `from_file_full` | IVUS rest/stress |
9
+ | 1 | Bruteforce vs. optimized at varying step sizes | `from_file_full` | IVUS rest/stress |
10
10
  | 2 | CPU-core scaling (parallelism) | `from_array_single` | OCT (280 frames) |
11
11
 
12
12
  Results land in `benchmarks/results/`.
13
13
 
14
14
  ---
15
15
 
16
- ## Benchmark 1 — Bruteforce vs. Optimised: Step-Size Scaling
16
+ ## Benchmark 1 — Bruteforce vs. optimized: Step-Size Scaling
17
17
 
18
18
  ### Goal
19
19
 
20
- Quantify how bruteforce and optimised alignment scale as `step_rotation_deg` decreases
20
+ Quantify how bruteforce and optimized alignment scale as `step_rotation_deg` decreases
21
21
  (i.e. as the rotation-search grid becomes finer).
22
22
 
23
23
  ### Setup
@@ -51,7 +51,7 @@ Corresponding number of rotation steps n = 180 / step (using the full ±90° ran
51
51
 
52
52
  - **Bruteforce**: exhaustively scores every rotation angle for every frame pair →
53
53
  wall time grows linearly with n\_steps (O(n)).
54
- - **Optimised**: coarse-to-fine hierarchical search → sub-linear in n\_steps,
54
+ - **optimized**: coarse-to-fine hierarchical search → sub-linear in n\_steps,
55
55
  asymptotically O(log n).
56
56
 
57
57
  The plot axes are logarithmic so the two complexity classes appear as straight lines
@@ -64,8 +64,8 @@ with clearly different slopes.
64
64
  - x-axis (bottom): number of rotation steps; (top): step size in degrees
65
65
  - y-axis: median wall time (seconds)
66
66
  - Line 1: bruteforce (red ○–) + O(n) reference dashed line
67
- - Line 2: optimised (blue ■ --)
68
- - Right y-axis: speedup factor (bruteforce / optimised)
67
+ - Line 2: optimized (blue ■ --)
68
+ - Right y-axis: speedup factor (bruteforce / optimized)
69
69
 
70
70
  ---
71
71
 
@@ -110,7 +110,7 @@ a **separate subprocess** with the variable set before any import of `multimodar
110
110
 
111
111
  ### Expected behaviour
112
112
 
113
- - **Optimised**: near-linear speedup up to ~8 cores, then diminishing returns
113
+ - **optimized**: near-linear speedup up to ~8 cores, then diminishing returns
114
114
  (Amdahl's law — serial fraction of the pipeline limits gains beyond that).
115
115
  - **Bruteforce**: also benefits from rayon frame-level parallelism, but expected
116
116
  to saturate earlier because the per-step work is less cache-friendly.
@@ -119,7 +119,7 @@ a **separate subprocess** with the variable set before any import of `multimodar
119
119
 
120
120
  `benchmarks/results/cpu_scaling.png` — two-panel figure:
121
121
 
122
- - Left panel: wall time (s) vs. CPU cores, two lines (bruteforce, optimised)
122
+ - Left panel: wall time (s) vs. CPU cores, two lines (bruteforce, optimized)
123
123
  - Right panel: speedup relative to 1 core vs. CPU cores +
124
124
  ideal linear-speedup reference; same two lines
125
125
 
@@ -1,4 +1,4 @@
1
- """Benchmark 1: bruteforce vs. optimised alignment at varying step sizes.
1
+ """Benchmark 1: bruteforce vs. optimized alignment at varying step sizes.
2
2
 
3
3
  Tests from_file_full on the bundled IVUS rest/stress example data with
4
4
  step_rotation_deg in {5.0, 2.5, 1.0, 0.5, 0.1} degrees.
@@ -27,7 +27,7 @@ INPUT_AB = str(DATA_ROOT / "ivus_rest")
27
27
  INPUT_CD = str(DATA_ROOT / "ivus_stress")
28
28
  RESULTS_DIR = Path(__file__).parent / "results"
29
29
 
30
- STEP_SIZES: list[float] = [5.0, 2.5, 1.0, 0.5, 0.1, 0.05]
30
+ STEP_SIZES: list[float] = [5.0, 2.5, 1.0, 0.5, 0.25, 0.1, 0.05]
31
31
  RANGE_DEG: float = 90.0
32
32
  REPEATS: int = 3
33
33
 
@@ -63,7 +63,7 @@ def _run_once(step: float, bruteforce: bool) -> float:
63
63
 
64
64
 
65
65
  def run_benchmark() -> dict[str, list[float]]:
66
- results: dict[str, list[float]] = {"bruteforce": [], "optimised": []}
66
+ results: dict[str, list[float]] = {"bruteforce": [], "optimized": []}
67
67
 
68
68
  for step in STEP_SIZES:
69
69
  print(f"step={step:4.1f}° bruteforce=True ", end="", flush=True)
@@ -75,7 +75,7 @@ def run_benchmark() -> dict[str, list[float]]:
75
75
  print(f"step={step:4.1f}° bruteforce=False ", end="", flush=True)
76
76
  times_opt = [_run_once(step, bruteforce=False) for _ in range(REPEATS)]
77
77
  med_opt = statistics.median(times_opt)
78
- results["optimised"].append(med_opt)
78
+ results["optimized"].append(med_opt)
79
79
  speedup = med_bf / med_opt if med_opt > 0 else float("inf")
80
80
  print(f"{med_opt:.2f} s (speedup {speedup:.1f}x)")
81
81
 
@@ -92,7 +92,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
92
92
 
93
93
  n_steps_list = [_n_steps(s) for s in STEP_SIZES]
94
94
  bf_times = results["bruteforce"]
95
- opt_times = results["optimised"]
95
+ opt_times = results["optimized"]
96
96
  speedups = [bf / opt for bf, opt in zip(bf_times, opt_times)]
97
97
 
98
98
  fig, (ax_main, ax_speedup) = plt.subplots(
@@ -116,7 +116,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
116
116
  color="tab:blue",
117
117
  linewidth=2,
118
118
  markersize=7,
119
- label="Optimised",
119
+ label="optimized",
120
120
  )
121
121
 
122
122
  # O(n) reference anchored at the first bruteforce point
@@ -137,7 +137,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
137
137
  ax_main.set_xlabel("Number of rotation steps (2 × range / step)", fontsize=12)
138
138
  ax_main.set_ylabel("Median wall time (s)", fontsize=12)
139
139
  ax_main.set_title(
140
- "Bruteforce vs. Optimised — Step-Size Scaling\n"
140
+ "Bruteforce vs. optimized — Step-Size Scaling\n"
141
141
  "from_file_full · IVUS rest/stress · range = 90°",
142
142
  fontsize=11,
143
143
  )
@@ -164,9 +164,9 @@ def plot_results(results: dict[str, list[float]]) -> None:
164
164
  ax_speedup.axhline(1.0, color="gray", linewidth=1, linestyle=":")
165
165
  ax_speedup.set_xscale("log")
166
166
  ax_speedup.set_xlabel("Number of rotation steps (2 × range / step)", fontsize=12)
167
- ax_speedup.set_ylabel("Speedup (bruteforce / optimised)", fontsize=12)
167
+ ax_speedup.set_ylabel("Speedup (bruteforce / optimized)", fontsize=12)
168
168
  ax_speedup.set_title(
169
- "Speedup of Optimised over Bruteforce\n"
169
+ "Speedup of optimized over Bruteforce\n"
170
170
  "from_file_full · IVUS rest/stress · range = 90°",
171
171
  fontsize=11,
172
172
  )
@@ -190,7 +190,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
190
190
  # ---------------------------------------------------------------------------
191
191
 
192
192
  if __name__ == "__main__":
193
- print("=== Benchmark 1: Bruteforce vs. Optimised — Step-Size Scaling ===\n")
193
+ print("=== Benchmark 1: Bruteforce vs. optimized — Step-Size Scaling ===\n")
194
194
  results = run_benchmark()
195
195
  print("\nPlotting results …")
196
196
  plot_results(results)
@@ -1,4 +1,4 @@
1
- """Benchmark 2: CPU-core scaling for optimised vs. bruteforce alignment.
1
+ """Benchmark 2: CPU-core scaling for optimized vs. bruteforce alignment.
2
2
 
3
3
  Tests from_array_single on the bundled OCT example dataset (280 frames) at
4
4
  varying RAYON_NUM_THREADS values (1 → 16 cores).
@@ -103,6 +103,7 @@ def _run_worker(n_cores: int, bruteforce: bool) -> list[float]:
103
103
  [sys.executable, "-c", script],
104
104
  capture_output=True,
105
105
  text=True,
106
+ encoding="utf-8",
106
107
  )
107
108
  if proc.returncode != 0 or not proc.stdout.strip():
108
109
  print(f"\n [stdout] {proc.stdout.strip()[:600]}", file=sys.stderr)
@@ -122,7 +123,7 @@ def _run_worker(n_cores: int, bruteforce: bool) -> list[float]:
122
123
 
123
124
 
124
125
  def run_benchmark() -> dict[str, list[float]]:
125
- results: dict[str, list[float]] = {"bruteforce": [], "optimised": []}
126
+ results: dict[str, list[float]] = {"bruteforce": [], "optimized": []}
126
127
 
127
128
  for n_cores in CORE_COUNTS:
128
129
  label_width = len(str(max(CORE_COUNTS)))
@@ -144,7 +145,7 @@ def run_benchmark() -> dict[str, list[float]]:
144
145
  )
145
146
  times_opt = _run_worker(n_cores, bruteforce=False)
146
147
  med_opt = statistics.median(times_opt)
147
- results["optimised"].append(med_opt)
148
+ results["optimized"].append(med_opt)
148
149
  print(f"{med_opt:.2f} s")
149
150
 
150
151
  return results
@@ -159,7 +160,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
159
160
  RESULTS_DIR.mkdir(parents=True, exist_ok=True)
160
161
 
161
162
  bf_times = results["bruteforce"]
162
- opt_times = results["optimised"]
163
+ opt_times = results["optimized"]
163
164
 
164
165
  bf_speedup = [bf_times[0] / t for t in bf_times]
165
166
  opt_speedup = [opt_times[0] / t for t in opt_times]
@@ -186,7 +187,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
186
187
  color="tab:blue",
187
188
  linewidth=2,
188
189
  markersize=7,
189
- label="Optimised",
190
+ label="optimized",
190
191
  )
191
192
  ax_time.set_xlabel("CPU cores (RAYON_NUM_THREADS)", fontsize=12)
192
193
  ax_time.set_ylabel("Median wall time (s)", fontsize=12)
@@ -224,7 +225,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
224
225
  color="tab:blue",
225
226
  linewidth=2,
226
227
  markersize=7,
227
- label="Optimised",
228
+ label="optimized",
228
229
  )
229
230
  ax_speedup.set_xlabel("CPU cores (RAYON_NUM_THREADS)", fontsize=12)
230
231
  ax_speedup.set_ylabel("Speedup (relative to 1 core)", fontsize=12)
@@ -1005,7 +1005,7 @@ def from_array_single(
1005
1005
 
1006
1006
  def align_three_point(
1007
1007
  centerline: PyCenterline,
1008
- geometry_pair: PyGeometryPair,
1008
+ geometry: PyGeometryPair | PyGeometry,
1009
1009
  aortic_ref_pt: tuple[float, float, float],
1010
1010
  upper_ref_pt: tuple[float, float, float],
1011
1011
  lower_ref_pt: tuple[float, float, float],
@@ -1016,20 +1016,19 @@ def align_three_point(
1016
1016
  output_dir: str = "output/aligned",
1017
1017
  contour_types: list[PyContourType] | None = None,
1018
1018
  case_name: str = "None",
1019
- ) -> tuple[PyGeometryPair, PyCenterline]:
1020
- """Align diastolic and systolic meshes to the centerline using three reference points.
1019
+ ) -> tuple[PyGeometryPair | PyGeometry, PyCenterline]:
1020
+ """Align a geometry (or geometry pair) to the centerline using three reference points.
1021
1021
 
1022
- Creates centerline-aligned meshes for a diastolic/systolic geometry pair
1023
- based on three anatomical reference points (aorta, upper section, lower
1024
- section). Only works for elliptic vessels such as coronary artery
1025
- anomalies.
1022
+ Creates centerline-aligned meshes based on three anatomical reference points
1023
+ (aorta, upper section, lower section). Only works for elliptic vessels such
1024
+ as coronary artery anomalies.
1026
1025
 
1027
1026
  Parameters
1028
1027
  ----------
1029
1028
  centerline : PyCenterline
1030
1029
  Centerline of the vessel.
1031
- geometry_pair : PyGeometryPair
1032
- Diastolic/systolic geometry pair to align.
1030
+ geometry : PyGeometryPair or PyGeometry
1031
+ Single geometry or diastolic/systolic geometry pair to align.
1033
1032
  aortic_ref_pt : tuple of float
1034
1033
  ``(x, y, z)`` reference point at the aortic ostium.
1035
1034
  upper_ref_pt : tuple of float
@@ -1043,7 +1042,8 @@ def align_three_point(
1043
1042
  watertight : bool, optional
1044
1043
  Whether to write a watertight or shell mesh. Default is ``True``.
1045
1044
  interpolation_steps : int, optional
1046
- Number of interpolation steps between phases. Default is ``28``.
1045
+ Number of interpolation steps between phases. Only used when *geometry*
1046
+ is a ``PyGeometryPair``. Default is ``0``.
1047
1047
  output_dir : str, optional
1048
1048
  Output directory for aligned meshes. Default is ``"output/aligned"``.
1049
1049
  contour_types : list of PyContourType, optional
@@ -1054,15 +1054,15 @@ def align_three_point(
1054
1054
 
1055
1055
  Returns
1056
1056
  -------
1057
- geom_pair : PyGeometryPair
1058
- Aligned geometry pair.
1057
+ geometry : PyGeometryPair or PyGeometry
1058
+ Aligned geometry, matching the type of the input.
1059
1059
  centerline : PyCenterline
1060
1060
  Resampled centerline.
1061
1061
 
1062
1062
  Examples
1063
1063
  --------
1064
1064
  >>> import multimodars as mm
1065
- >>> pair, cl = mm.align_three_point(
1065
+ >>> result, cl = mm.align_three_point(
1066
1066
  ... centerline,
1067
1067
  ... geometry_pair,
1068
1068
  ... (12.2605, -201.3643, 1751.0554),
@@ -1074,7 +1074,7 @@ def align_three_point(
1074
1074
  contour_types = _default_contour_types()
1075
1075
  return _align_three_point(
1076
1076
  centerline,
1077
- geometry_pair,
1077
+ geometry,
1078
1078
  aortic_ref_pt,
1079
1079
  upper_ref_pt,
1080
1080
  lower_ref_pt,
@@ -1090,7 +1090,7 @@ def align_three_point(
1090
1090
 
1091
1091
  def align_manual(
1092
1092
  centerline: PyCenterline,
1093
- geometry_pair: PyGeometryPair,
1093
+ geometry: PyGeometryPair | PyGeometry,
1094
1094
  rotation_angle: float,
1095
1095
  ref_point: tuple[float, float, float],
1096
1096
  write: bool = False,
@@ -1099,20 +1099,19 @@ def align_manual(
1099
1099
  output_dir: str = "output/aligned",
1100
1100
  contour_types: list[PyContourType] | None = None,
1101
1101
  case_name: str = "None",
1102
- ) -> tuple[PyGeometryPair, PyCenterline]:
1103
- """Align diastolic and systolic meshes to the centerline using a manual rotation angle.
1102
+ ) -> tuple[PyGeometryPair | PyGeometry, PyCenterline]:
1103
+ """Align a geometry (or geometry pair) to the centerline using a manual rotation angle.
1104
1104
 
1105
- Creates centerline-aligned meshes for a diastolic/systolic geometry pair
1106
- using an explicit rotation angle and a single reference point on the
1107
- centerline. Only works for elliptic vessels such as coronary artery
1108
- anomalies.
1105
+ Creates centerline-aligned meshes using an explicit rotation angle and a
1106
+ single reference point on the centerline. Only works for elliptic vessels
1107
+ such as coronary artery anomalies.
1109
1108
 
1110
1109
  Parameters
1111
1110
  ----------
1112
1111
  centerline : PyCenterline
1113
1112
  Centerline of the vessel.
1114
- geometry_pair : PyGeometryPair
1115
- Diastolic/systolic geometry pair to align.
1113
+ geometry : PyGeometryPair or PyGeometry
1114
+ Single geometry or diastolic/systolic geometry pair to align.
1116
1115
  rotation_angle : float
1117
1116
  Rotation angle in radians to apply.
1118
1117
  ref_point : tuple of float
@@ -1122,7 +1121,8 @@ def align_manual(
1122
1121
  watertight : bool, optional
1123
1122
  Whether to write a watertight or shell mesh. Default is ``True``.
1124
1123
  interpolation_steps : int, optional
1125
- Number of interpolation steps between phases. Default is ``28``.
1124
+ Number of interpolation steps between phases. Only used when *geometry*
1125
+ is a ``PyGeometryPair``. Default is ``0``.
1126
1126
  output_dir : str, optional
1127
1127
  Output directory for aligned meshes. Default is ``"output/aligned"``.
1128
1128
  contour_types : list of PyContourType, optional
@@ -1133,15 +1133,15 @@ def align_manual(
1133
1133
 
1134
1134
  Returns
1135
1135
  -------
1136
- geom_pair : PyGeometryPair
1137
- Aligned geometry pair.
1136
+ geometry : PyGeometryPair or PyGeometry
1137
+ Aligned geometry, matching the type of the input.
1138
1138
  centerline : PyCenterline
1139
1139
  Resampled centerline.
1140
1140
 
1141
1141
  Examples
1142
1142
  --------
1143
1143
  >>> import multimodars as mm
1144
- >>> pair, cl = mm.align_manual(
1144
+ >>> result, cl = mm.align_manual(
1145
1145
  ... centerline, geometry_pair, rotation_angle=1.57, ref_point=(1.0, 2.0, 3.0)
1146
1146
  ... )
1147
1147
  """
@@ -1149,7 +1149,7 @@ def align_manual(
1149
1149
  contour_types = _default_contour_types()
1150
1150
  return _align_manual(
1151
1151
  centerline,
1152
- geometry_pair,
1152
+ geometry,
1153
1153
  rotation_angle,
1154
1154
  ref_point,
1155
1155
  write,
@@ -1163,7 +1163,7 @@ def align_manual(
1163
1163
 
1164
1164
  def align_combined(
1165
1165
  centerline: PyCenterline,
1166
- geom_pair: PyGeometryPair,
1166
+ geometry: PyGeometryPair | PyGeometry,
1167
1167
  aortic_ref_pt: tuple[float, float, float],
1168
1168
  upper_ref_pt: tuple[float, float, float],
1169
1169
  lower_ref_pt: tuple[float, float, float],
@@ -1177,20 +1177,19 @@ def align_combined(
1177
1177
  output_dir: str = "output/aligned",
1178
1178
  contour_types: list[PyContourType] | None = None,
1179
1179
  case_name: str = "None",
1180
- ) -> tuple[PyGeometryPair, PyCenterline]:
1181
- """Align meshes using three reference points combined with Hausdorff distance refinement.
1180
+ ) -> tuple[PyGeometryPair | PyGeometry, PyCenterline]:
1181
+ """Align a geometry (or geometry pair) using three reference points and Hausdorff refinement.
1182
1182
 
1183
- Creates centerline-aligned meshes for a diastolic/systolic geometry pair
1184
- using three anatomical reference points for an initial orientation and a
1185
- set of additional points for Hausdorff distance-based fine-tuning of the
1186
- rotation.
1183
+ Creates centerline-aligned meshes using three anatomical reference points
1184
+ for an initial orientation and a set of additional points for
1185
+ Hausdorff distance-based fine-tuning of the rotation.
1187
1186
 
1188
1187
  Parameters
1189
1188
  ----------
1190
1189
  centerline : PyCenterline
1191
1190
  Centerline of the vessel.
1192
- geom_pair : PyGeometryPair
1193
- Diastolic/systolic geometry pair to align.
1191
+ geometry : PyGeometryPair or PyGeometry
1192
+ Single geometry or diastolic/systolic geometry pair to align.
1194
1193
  aortic_ref_pt : tuple of float
1195
1194
  ``(x, y, z)`` reference point at the aortic ostium.
1196
1195
  upper_ref_pt : tuple of float
@@ -1212,7 +1211,8 @@ def align_combined(
1212
1211
  watertight : bool, optional
1213
1212
  Whether to write a watertight or shell mesh. Default is ``True``.
1214
1213
  interpolation_steps : int, optional
1215
- Number of interpolation steps between phases. Default is ``28``.
1214
+ Number of interpolation steps between phases. Only used when *geometry*
1215
+ is a ``PyGeometryPair``. Default is ``0``.
1216
1216
  output_dir : str, optional
1217
1217
  Output directory for aligned meshes. Default is ``"output/aligned"``.
1218
1218
  contour_types : list of PyContourType, optional
@@ -1223,15 +1223,15 @@ def align_combined(
1223
1223
 
1224
1224
  Returns
1225
1225
  -------
1226
- geom_pair : PyGeometryPair
1227
- Aligned geometry pair.
1226
+ geometry : PyGeometryPair or PyGeometry
1227
+ Aligned geometry, matching the type of the input.
1228
1228
  centerline : PyCenterline
1229
1229
  Resampled centerline.
1230
1230
 
1231
1231
  Examples
1232
1232
  --------
1233
1233
  >>> import multimodars as mm
1234
- >>> pair, cl = mm.align_combined(
1234
+ >>> result, cl = mm.align_combined(
1235
1235
  ... centerline,
1236
1236
  ... geometry_pair,
1237
1237
  ... (12.2605, -201.3643, 1751.0554),
@@ -1244,7 +1244,7 @@ def align_combined(
1244
1244
  contour_types = _default_contour_types()
1245
1245
  return _align_combined(
1246
1246
  centerline,
1247
- geom_pair,
1247
+ geometry,
1248
1248
  aortic_ref_pt,
1249
1249
  upper_ref_pt,
1250
1250
  lower_ref_pt,