qsiplan 0.1.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 (110) hide show
  1. qsiplan-0.1.0/.github/workflows/lint.yml +41 -0
  2. qsiplan-0.1.0/.github/workflows/release.yml +46 -0
  3. qsiplan-0.1.0/.github/workflows/reusable-tests.yml +34 -0
  4. qsiplan-0.1.0/.github/workflows/tests.yml +15 -0
  5. qsiplan-0.1.0/.gitignore +35 -0
  6. qsiplan-0.1.0/LICENSE +27 -0
  7. qsiplan-0.1.0/PKG-INFO +61 -0
  8. qsiplan-0.1.0/README.md +18 -0
  9. qsiplan-0.1.0/codecov.yml +20 -0
  10. qsiplan-0.1.0/pyproject.toml +94 -0
  11. qsiplan-0.1.0/qsiplan/__init__.py +220 -0
  12. qsiplan-0.1.0/qsiplan/__main__.py +8 -0
  13. qsiplan-0.1.0/qsiplan/_version.py +24 -0
  14. qsiplan-0.1.0/qsiplan/adapters.py +438 -0
  15. qsiplan-0.1.0/qsiplan/cli.py +194 -0
  16. qsiplan-0.1.0/qsiplan/data/__init__.py +13 -0
  17. qsiplan-0.1.0/qsiplan/data/pipeline_viewer.css +16 -0
  18. qsiplan-0.1.0/qsiplan/data/pipeline_viewer.js +348 -0
  19. qsiplan-0.1.0/qsiplan/data/qspace_viewer.css +47 -0
  20. qsiplan-0.1.0/qsiplan/data/qspace_viewer.js +516 -0
  21. qsiplan-0.1.0/qsiplan/inference.py +1233 -0
  22. qsiplan-0.1.0/qsiplan/integrity.py +275 -0
  23. qsiplan-0.1.0/qsiplan/interactive.py +773 -0
  24. qsiplan-0.1.0/qsiplan/metadata.py +379 -0
  25. qsiplan-0.1.0/qsiplan/methods.py +359 -0
  26. qsiplan-0.1.0/qsiplan/models.py +571 -0
  27. qsiplan-0.1.0/qsiplan/plan.py +613 -0
  28. qsiplan-0.1.0/qsiplan/report.py +977 -0
  29. qsiplan-0.1.0/qsiplan/validation.py +327 -0
  30. qsiplan-0.1.0/qsiplan/viz/__init__.py +1 -0
  31. qsiplan-0.1.0/qsiplan/viz/pipeline.py +156 -0
  32. qsiplan-0.1.0/qsiplan/viz/qspace.py +93 -0
  33. qsiplan-0.1.0/tests/conftest.py +1 -0
  34. qsiplan-0.1.0/tests/data/grouping_reports/abcd_style.txt +52 -0
  35. qsiplan-0.1.0/tests/data/grouping_reports/cross_axis_b0field.txt +66 -0
  36. qsiplan-0.1.0/tests/data/grouping_reports/cross_axis_unpaired.txt +56 -0
  37. qsiplan-0.1.0/tests/data/grouping_reports/fieldmapless_t1w_only_syn.txt +46 -0
  38. qsiplan-0.1.0/tests/data/grouping_reports/fieldmapless_t1w_only_synb0.txt +48 -0
  39. qsiplan-0.1.0/tests/data/grouping_reports/fieldmapless_t2w.txt +47 -0
  40. qsiplan-0.1.0/tests/data/grouping_reports/hcp_style.txt +57 -0
  41. qsiplan-0.1.0/tests/data/grouping_reports/missing_pedir.txt +81 -0
  42. qsiplan-0.1.0/tests/data/grouping_reports/multi_readout.txt +66 -0
  43. qsiplan-0.1.0/tests/data/grouping_reports/multi_session_b0field_reused.txt +145 -0
  44. qsiplan-0.1.0/tests/data/grouping_reports/multi_session_curated_multipart.txt +90 -0
  45. qsiplan-0.1.0/tests/data/grouping_reports/multi_session_shared_fmap.txt +88 -0
  46. qsiplan-0.1.0/tests/data/grouping_reports/multipart_splits_estimation.txt +105 -0
  47. qsiplan-0.1.0/tests/data/grouping_reports/nonshelled_pair.txt +55 -0
  48. qsiplan-0.1.0/tests/data/grouping_reports/partial_curation.txt +118 -0
  49. qsiplan-0.1.0/tests/data/grouping_reports/partial_pair.txt +64 -0
  50. qsiplan-0.1.0/tests/data/grouping_reports/shell_mix.txt +60 -0
  51. qsiplan-0.1.0/tests/data/grouping_reports/t2w_hcp_synb0.txt +56 -0
  52. qsiplan-0.1.0/tests/data/grouping_reports/two_gre_fmaps.txt +70 -0
  53. qsiplan-0.1.0/tests/data/grouping_reports/virtual_acq_isolated_fields.txt +151 -0
  54. qsiplan-0.1.0/tests/data/grouping_reports/virtual_acq_multipart.txt +93 -0
  55. qsiplan-0.1.0/tests/data/skeleton_complex_relpaths.yml +38 -0
  56. qsiplan-0.1.0/tests/data/skeleton_grouping_abcd_style.yml +20 -0
  57. qsiplan-0.1.0/tests/data/skeleton_grouping_abcd_t2w.yml +21 -0
  58. qsiplan-0.1.0/tests/data/skeleton_grouping_acq_multipart.yml +37 -0
  59. qsiplan-0.1.0/tests/data/skeleton_grouping_b0only_fmap_with_bvals.yml +25 -0
  60. qsiplan-0.1.0/tests/data/skeleton_grouping_bidsuri_intendedfor.yml +19 -0
  61. qsiplan-0.1.0/tests/data/skeleton_grouping_cluster_multipart.yml +65 -0
  62. qsiplan-0.1.0/tests/data/skeleton_grouping_cluster_nomultipart.yml +54 -0
  63. qsiplan-0.1.0/tests/data/skeleton_grouping_cross_axis_b0field.yml +36 -0
  64. qsiplan-0.1.0/tests/data/skeleton_grouping_cross_axis_unpaired.yml +19 -0
  65. qsiplan-0.1.0/tests/data/skeleton_grouping_curated_b0field.yml +21 -0
  66. qsiplan-0.1.0/tests/data/skeleton_grouping_curated_t2wreg.yml +17 -0
  67. qsiplan-0.1.0/tests/data/skeleton_grouping_fieldmapless_t1w_only.yml +13 -0
  68. qsiplan-0.1.0/tests/data/skeleton_grouping_fieldmapless_t2w.yml +14 -0
  69. qsiplan-0.1.0/tests/data/skeleton_grouping_fov_grid.yml +17 -0
  70. qsiplan-0.1.0/tests/data/skeleton_grouping_fov_oblique.yml +18 -0
  71. qsiplan-0.1.0/tests/data/skeleton_grouping_fov_shift.yml +18 -0
  72. qsiplan-0.1.0/tests/data/skeleton_grouping_gre_phasediff.yml +25 -0
  73. qsiplan-0.1.0/tests/data/skeleton_grouping_hcp_style.yml +31 -0
  74. qsiplan-0.1.0/tests/data/skeleton_grouping_intendedfor_superseded.yml +35 -0
  75. qsiplan-0.1.0/tests/data/skeleton_grouping_maxb_mismatch.yml +17 -0
  76. qsiplan-0.1.0/tests/data/skeleton_grouping_missing_pedir.yml +16 -0
  77. qsiplan-0.1.0/tests/data/skeleton_grouping_mixed_trt.yml +18 -0
  78. qsiplan-0.1.0/tests/data/skeleton_grouping_multi_readout.yml +40 -0
  79. qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session.yml +29 -0
  80. qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session_b0field_reused.yml +37 -0
  81. qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session_curated_multipart.yml +43 -0
  82. qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session_shared_fmap.yml +36 -0
  83. qsiplan-0.1.0/tests/data/skeleton_grouping_multipart_splits_estimation.yml +35 -0
  84. qsiplan-0.1.0/tests/data/skeleton_grouping_name_collision.yml +36 -0
  85. qsiplan-0.1.0/tests/data/skeleton_grouping_name_collision_inferred.yml +36 -0
  86. qsiplan-0.1.0/tests/data/skeleton_grouping_nonshelled_pair.yml +18 -0
  87. qsiplan-0.1.0/tests/data/skeleton_grouping_partial_curation.yml +35 -0
  88. qsiplan-0.1.0/tests/data/skeleton_grouping_partial_curation_stranded.yml +32 -0
  89. qsiplan-0.1.0/tests/data/skeleton_grouping_partial_intendedfor.yml +26 -0
  90. qsiplan-0.1.0/tests/data/skeleton_grouping_partial_multipart.yml +34 -0
  91. qsiplan-0.1.0/tests/data/skeleton_grouping_partial_pair.yml +34 -0
  92. qsiplan-0.1.0/tests/data/skeleton_grouping_reshim.yml +29 -0
  93. qsiplan-0.1.0/tests/data/skeleton_grouping_same_ped_own_fmaps.yml +35 -0
  94. qsiplan-0.1.0/tests/data/skeleton_grouping_shell_mix.yml +20 -0
  95. qsiplan-0.1.0/tests/data/skeleton_grouping_t2w_hcp.yml +20 -0
  96. qsiplan-0.1.0/tests/data/skeleton_grouping_two_gre_fmaps.yml +42 -0
  97. qsiplan-0.1.0/tests/data/skeleton_grouping_unlinked_fmap.yml +24 -0
  98. qsiplan-0.1.0/tests/data/skeleton_grouping_virtual_acq_isolated_fields.yml +51 -0
  99. qsiplan-0.1.0/tests/data/skeleton_grouping_virtual_acq_multipart.yml +26 -0
  100. qsiplan-0.1.0/tests/data/skeleton_simple_multiped.yml +22 -0
  101. qsiplan-0.1.0/tests/grouping_scenarios.py +190 -0
  102. qsiplan-0.1.0/tests/test_cli.py +60 -0
  103. qsiplan-0.1.0/tests/test_grouping_adapters.py +335 -0
  104. qsiplan-0.1.0/tests/test_grouping_inference.py +1048 -0
  105. qsiplan-0.1.0/tests/test_grouping_integrity.py +54 -0
  106. qsiplan-0.1.0/tests/test_grouping_methods.py +136 -0
  107. qsiplan-0.1.0/tests/test_grouping_models.py +237 -0
  108. qsiplan-0.1.0/tests/test_grouping_plan.py +294 -0
  109. qsiplan-0.1.0/tests/test_grouping_report.py +158 -0
  110. qsiplan-0.1.0/tests/test_grouping_viz.py +287 -0
@@ -0,0 +1,41 @@
1
+ name: Contribution checks
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ defaults:
12
+ run:
13
+ shell: bash
14
+
15
+ concurrency:
16
+ group: ${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ style:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ # Keep this pin in sync with the ruff rev in .pre-commit-config.yaml
28
+ # and the dev pin in pyproject.toml. Unpinned, CI silently follows new
29
+ # ruff releases and goes red without a code change.
30
+ - run: pipx run --spec ruff==0.15.21 ruff check .
31
+ - run: pipx run --spec ruff==0.15.21 ruff format --diff .
32
+
33
+ codespell:
34
+ name: Check for spelling errors
35
+ runs-on: ubuntu-latest
36
+
37
+ steps:
38
+ - name: Checkout
39
+ uses: actions/checkout@v7
40
+ - name: Codespell
41
+ uses: codespell-project/actions-codespell@v2
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ # The release runs the same suite as every push/PR, re-run on the exact
12
+ # tagged commit. publish depends on it, so a red tag never reaches PyPI.
13
+ tests:
14
+ uses: ./.github/workflows/reusable-tests.yml
15
+ secrets: inherit
16
+
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0 # hatch-vcs derives the version from the tag
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: '3.11'
26
+ - run: pipx run build
27
+ - run: pipx run twine check dist/*
28
+ - uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: [tests, build]
35
+ runs-on: ubuntu-latest
36
+ environment:
37
+ name: pypi
38
+ url: https://pypi.org/p/qsiplan
39
+ permissions:
40
+ id-token: write # OIDC token for Trusted Publishing (no stored secret)
41
+ steps:
42
+ - uses: actions/download-artifact@v4
43
+ with:
44
+ name: dist
45
+ path: dist/
46
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ name: reusable tests
2
+
3
+ on:
4
+ workflow_call:
5
+ secrets:
6
+ CODECOV_TOKEN:
7
+ required: false
8
+
9
+ jobs:
10
+ pytest:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ['3.11', '3.12', '3.13']
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ cache: pip
22
+ - name: Install with test extras
23
+ run: pip install -e '.[tests]' pytest-cov
24
+ - name: Run the suite with coverage
25
+ run: pytest tests --cov=qsiplan --cov-report=term --cov-report=xml --cov-fail-under=85
26
+ - name: Smoke the CLI entry point
27
+ run: qsiplan --help
28
+ - name: Upload coverage to Codecov
29
+ uses: codecov/codecov-action@v5
30
+ with:
31
+ token: ${{ secrets.CODECOV_TOKEN }}
32
+ files: ./coverage.xml
33
+ flags: py${{ matrix.python-version }}
34
+ fail_ci_if_error: false
@@ -0,0 +1,15 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: tests-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ tests:
14
+ uses: ./.github/workflows/reusable-tests.yml
15
+ secrets: inherit
@@ -0,0 +1,35 @@
1
+ # Byte-compiled / caches
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .hypothesis/
6
+
7
+ # Distribution / packaging
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ .eggs/
12
+
13
+ # Version file, if hatch-vcs versioning is adopted
14
+ qsiplan/_version.py
15
+
16
+ # Coverage
17
+ htmlcov/
18
+ .coverage
19
+ .coverage.*
20
+ coverage.xml
21
+
22
+ # Environments
23
+ .venv/
24
+ env/
25
+ .tox/
26
+
27
+ # Editors / OS
28
+ .idea/
29
+ .vscode/
30
+ .ipynb_checkpoints/
31
+ *.swp
32
+ *.DS_Store
33
+
34
+ # Rendered preview pages from local qsiplan runs
35
+ grouping*.html
qsiplan-0.1.0/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2015-2018, the PennLINC developers team.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of qsiprep nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
qsiplan-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.5
2
+ Name: qsiplan
3
+ Version: 0.1.0
4
+ Summary: Plan and explain QSIPrep's diffusion-MRI preprocessing from BIDS metadata
5
+ Project-URL: Homepage, https://github.com/PennLINC/qsiprep
6
+ Author: The PennLINC developers
7
+ License: Copyright (c) 2015-2018, the PennLINC developers team.
8
+ All rights reserved.
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions are met:
12
+
13
+ * Redistributions of source code must retain the above copyright notice, this
14
+ list of conditions and the following disclaimer.
15
+
16
+ * Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+
20
+ * Neither the name of qsiprep nor the names of its
21
+ contributors may be used to endorse or promote products derived from
22
+ this software without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ License-File: LICENSE
35
+ Requires-Python: >=3.11
36
+ Requires-Dist: nibabel
37
+ Requires-Dist: numpy
38
+ Requires-Dist: pybids
39
+ Provides-Extra: tests
40
+ Requires-Dist: niworkflows; extra == 'tests'
41
+ Requires-Dist: pytest; extra == 'tests'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # QSIPlan
45
+
46
+ [![tests](https://github.com/PennLINC/QSIPlan/actions/workflows/tests.yml/badge.svg)](https://github.com/PennLINC/QSIPlan/actions/workflows/tests.yml)
47
+ [![codecov](https://codecov.io/gh/PennLINC/QSIPlan/branch/main/graph/badge.svg)](https://codecov.io/gh/PennLINC/QSIPlan)
48
+
49
+ Plan and explain [qsiprep](https://github.com/PennLINC/qsiprep)'s
50
+ diffusion-MRI preprocessing from BIDS metadata and configuration flags.
51
+
52
+ `qsiplan` groups a subject's DWI scans the way qsiprep will (distortion
53
+ groups, fieldmap estimations, correction units, outputs), validates the
54
+ data and curation (missing phase-encoding metadata, IntendedFor conflicts,
55
+ shim walls, b-value mismatches...), compiles the execution plan for any
56
+ combination of head-motion- and susceptibility-correction methods, and
57
+ renders it all as text or as a self-contained interactive HTML page:
58
+
59
+ ```bash
60
+ qsiplan /path/to/bids --html grouping.html
61
+ ```
@@ -0,0 +1,18 @@
1
+ # QSIPlan
2
+
3
+ [![tests](https://github.com/PennLINC/QSIPlan/actions/workflows/tests.yml/badge.svg)](https://github.com/PennLINC/QSIPlan/actions/workflows/tests.yml)
4
+ [![codecov](https://codecov.io/gh/PennLINC/QSIPlan/branch/main/graph/badge.svg)](https://codecov.io/gh/PennLINC/QSIPlan)
5
+
6
+ Plan and explain [qsiprep](https://github.com/PennLINC/qsiprep)'s
7
+ diffusion-MRI preprocessing from BIDS metadata and configuration flags.
8
+
9
+ `qsiplan` groups a subject's DWI scans the way qsiprep will (distortion
10
+ groups, fieldmap estimations, correction units, outputs), validates the
11
+ data and curation (missing phase-encoding metadata, IntendedFor conflicts,
12
+ shim walls, b-value mismatches...), compiles the execution plan for any
13
+ combination of head-motion- and susceptibility-correction methods, and
14
+ renders it all as text or as a self-contained interactive HTML page:
15
+
16
+ ```bash
17
+ qsiplan /path/to/bids --html grouping.html
18
+ ```
@@ -0,0 +1,20 @@
1
+ # Codecov behaves as a reporter here, never a gate: the enforced floor is
2
+ # pytest's --cov-fail-under in CI, so both statuses are informational and
3
+ # nothing goes red because of an upload or rounding quirk.
4
+ coverage:
5
+ status:
6
+ project:
7
+ default:
8
+ informational: true
9
+ patch:
10
+ default:
11
+ informational: true
12
+
13
+ comment:
14
+ layout: "condensed_header, diff, flags"
15
+ # One comment after all three matrix uploads, not three drafts.
16
+ after_n_builds: 3
17
+
18
+ codecov:
19
+ notify:
20
+ after_n_builds: 3
@@ -0,0 +1,94 @@
1
+ [build-system]
2
+ requires = ["hatchling", "hatch-vcs"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "qsiplan"
7
+ dynamic = ["version"]
8
+ description = "Plan and explain QSIPrep's diffusion-MRI preprocessing from BIDS metadata"
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ requires-python = ">=3.11"
12
+ authors = [{ name = "The PennLINC developers" }]
13
+ dependencies = [
14
+ "pybids",
15
+ "nibabel",
16
+ "numpy",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ tests = [
21
+ "pytest",
22
+ "niworkflows",
23
+ ]
24
+
25
+ [project.scripts]
26
+ qsiplan = "qsiplan.cli:main"
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/PennLINC/qsiprep"
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["qsiplan"]
33
+
34
+ [tool.hatch.version]
35
+ source = "vcs"
36
+ fallback-version = "0.1.0"
37
+
38
+ [tool.hatch.build.hooks.vcs]
39
+ version-file = "qsiplan/_version.py"
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
43
+
44
+ [tool.coverage.run]
45
+ branch = true
46
+ source = ["qsiplan"]
47
+
48
+ [tool.coverage.report]
49
+ show_missing = true
50
+
51
+ [tool.ruff]
52
+ line-length = 99
53
+ extend-exclude = ["qsiplan/_version.py"]
54
+
55
+ [tool.ruff.lint]
56
+ extend-select = [
57
+ "F",
58
+ "E",
59
+ "W",
60
+ "I",
61
+ "UP",
62
+ "YTT",
63
+ "S",
64
+ "BLE",
65
+ "B",
66
+ "A",
67
+ "C4",
68
+ "DTZ",
69
+ "T10",
70
+ "EXE",
71
+ "FA",
72
+ "ISC",
73
+ "ICN",
74
+ "PT",
75
+ "Q",
76
+ ]
77
+ ignore = [
78
+ "ISC001", # conflicts with the formatter
79
+ "S311", # random is never used for cryptography here
80
+ ]
81
+
82
+ [tool.ruff.lint.flake8-quotes]
83
+ inline-quotes = "single"
84
+
85
+ [tool.ruff.lint.extend-per-file-ignores]
86
+ "tests/*.py" = ["S101"]
87
+
88
+ [tool.ruff.format]
89
+ quote-style = "single"
90
+
91
+ [tool.codespell]
92
+ skip = '.git,*.html,*.svg,*.lock'
93
+ # te: the TE (echo time) abbreviation in prose and golden reports
94
+ ignore-words-list = 'te'
@@ -0,0 +1,220 @@
1
+ """BIDS-native grouping of DWI scans for preprocessing.
2
+
3
+ This package decides, per subject, from BIDS metadata alone:
4
+
5
+ 1. Which DWI files share susceptibility distortions (distortion groups).
6
+ 2. Which files jointly estimate each fieldmap (``B0FieldIdentifier``).
7
+ 3. Which fieldmap corrects which DWI file (``B0FieldSource``).
8
+ 4. Which files are concatenated in the outputs (``MultipartID``).
9
+
10
+ Curated sidecar metadata is used verbatim; everything the user did not curate
11
+ is inferred and tagged with its provenance. The output,
12
+ :class:`~.models.DWIGrouping`, describes the *data* - how any given
13
+ processing backend arranges that data is the business of adapters and the
14
+ previews in :mod:`~.report`.
15
+
16
+ Run ``qsiplan /path/to/bids`` to print the grouping and
17
+ per-selection processing previews for a dataset.
18
+
19
+ Adding a new estimation method
20
+ ------------------------------
21
+ The (method x selection) behavior matrix is deliberately spelled out as prose
22
+ branches rather than a rules table, because the preview text is the product.
23
+ The cost is that a new :class:`~.models.CorrectionMethod` touches five places
24
+ (SYNB0 is the worked example in each):
25
+
26
+ 1. :class:`~.models.CorrectionMethod` - add the member.
27
+ 2. ``inference.py`` - produce it: ``_classify_method`` for curated sources
28
+ and/or a step in ``resolve_fieldmapless``; rank it in ``_METHOD_RANK``.
29
+ 3. ``methods.py`` - which :class:`~.methods.SdcTool` consumes it (the
30
+ capability registries), and ``plan.py`` - its stage sequence per HMC
31
+ method, and whether an infeasible selection is an error or a degradation.
32
+ 4. ``report.py`` - ``_METHOD_LABELS``, ``MethodGroups``/``_ids_by_kind``,
33
+ a narration branch in each ``_describe_*`` function, and its
34
+ ``_stage_text`` sentence.
35
+ 5. Tests - a scenario skeleton, inference assertions, plan-stage assertions,
36
+ and regenerated golden reports (``QSIPREP_REGEN_GROUPING_REPORTS=1``).
37
+ """
38
+
39
+ try:
40
+ from ._version import __version__
41
+ except ImportError: # pragma: no cover - not yet built from a git checkout
42
+ __version__ = '0+unknown'
43
+
44
+ from .adapters import (
45
+ PreprocUnit,
46
+ assembly_to_sidecar,
47
+ concatenation_scheme,
48
+ to_preproc_units,
49
+ unit_to_sidecar,
50
+ )
51
+ from .inference import build_grouping
52
+ from .interactive import render_html, render_report_segment
53
+ from .metadata import index_subject
54
+ from .methods import (
55
+ HMC_CAPABILITIES,
56
+ SDC_CAPABILITIES,
57
+ HmcMethod,
58
+ MethodSelection,
59
+ SdcTool,
60
+ canonical_selection,
61
+ selection_for_config,
62
+ )
63
+ from .models import (
64
+ ConcatenationGroup,
65
+ CorrectionMethod,
66
+ DistortionGroup,
67
+ DistortionSignature,
68
+ DWIGrouping,
69
+ FieldmapEstimation,
70
+ FileRecord,
71
+ GroupingPolicy,
72
+ Provenance,
73
+ )
74
+ from .plan import ExecutionPlan, OutputAssembly, PlanStage, ProcessingRun, compile_plan
75
+ from .report import describe_processing, full_report, report_text
76
+ from .validation import BACKENDS, GroupingError, GroupingIssue, check_backend, raise_for_errors
77
+
78
+ __all__ = [
79
+ 'BACKENDS',
80
+ 'HMC_CAPABILITIES',
81
+ 'SDC_CAPABILITIES',
82
+ 'ConcatenationGroup',
83
+ 'DistortionGroup',
84
+ 'DistortionSignature',
85
+ 'DWIGrouping',
86
+ 'CorrectionMethod',
87
+ 'FieldmapEstimation',
88
+ 'FileRecord',
89
+ 'GroupingError',
90
+ 'GroupingIssue',
91
+ 'GroupingPolicy',
92
+ 'HmcMethod',
93
+ 'MethodSelection',
94
+ 'PreprocUnit',
95
+ 'Provenance',
96
+ 'SdcTool',
97
+ 'assembly_to_sidecar',
98
+ 'build_dwi_grouping',
99
+ 'canonical_selection',
100
+ 'check_backend',
101
+ 'compile_plan',
102
+ 'ExecutionPlan',
103
+ 'OutputAssembly',
104
+ 'PlanStage',
105
+ 'ProcessingRun',
106
+ 'concatenation_scheme',
107
+ 'describe_processing',
108
+ 'full_report',
109
+ 'index_subject',
110
+ 'render_html',
111
+ 'render_report_segment',
112
+ 'report_text',
113
+ 'selection_for_config',
114
+ 'to_preproc_units',
115
+ 'unit_to_sidecar',
116
+ ]
117
+
118
+
119
+ def build_dwi_grouping(
120
+ layout,
121
+ subject_data,
122
+ separate_all_dwis=False,
123
+ ignore_fieldmaps=False,
124
+ ignore_shims=False,
125
+ ignore_fov=False,
126
+ ignore_sdc=False,
127
+ force_t2wreg=False,
128
+ use_synb0=False,
129
+ use_nipreps_syn_sdc=False,
130
+ distortion_group_merge='concat',
131
+ b0_threshold=None,
132
+ strict=True,
133
+ ):
134
+ """Group one subject's DWI scans.
135
+
136
+ Parameters
137
+ ----------
138
+ layout : :class:`bids.BIDSLayout`
139
+ Layout of the input dataset; used only for sidecar metadata reads.
140
+ subject_data : dict
141
+ As returned by :func:`qsiprep.utils.bids.collect_data`: must contain
142
+ a ``'dwi'`` key listing the subject's DWI files (``'fmap'``,
143
+ ``'t1w'``, and ``'t2w'`` are optional - they are discovered from the
144
+ layout when absent).
145
+ separate_all_dwis : bool
146
+ Every DWI series becomes its own output. Fieldmap estimation still
147
+ happens at session scope, so single series keep their SDC.
148
+ ignore_fieldmaps : bool
149
+ Do not index ``fmap/``. The reverse phase-encoding DWI heuristic
150
+ still applies.
151
+ ignore_shims : bool
152
+ Treat all ShimSetting values as compatible. Use when data were
153
+ re-shimmed but distortion correction across shims is wanted anyway.
154
+ ignore_fov : bool
155
+ Downgrade the differing-orientation field-of-view error to a warning
156
+ and proceed, accepting misapplied distortion corrections. Grid-size
157
+ mismatches remain errors: they cannot be stacked at all.
158
+ ignore_sdc : bool
159
+ Disable susceptibility distortion correction entirely: no fieldmaps, no
160
+ reverse-PE heuristic, and no fieldmap-less fallback. Every series is
161
+ left uncorrected but is still grouped and concatenated for HMC. Stronger
162
+ than ``ignore_fieldmaps`` (which only skips ``fmap/``).
163
+ force_t2wreg : bool
164
+ Correct every DWI series by registering its b=0 to the subject's T2w
165
+ (TORTOISE T2Wreg), overriding any fieldmaps. Errors if no T2w exists.
166
+ use_synb0 : bool
167
+ Give series that have no fieldmap a SyNb0 synthetic-b=0 estimation
168
+ synthesized from the T1w. Never overrides a real fieldmap. Errors if
169
+ no T1w exists or a target series lacks PhaseEncodingDirection.
170
+ distortion_group_merge : str or None
171
+ How the corrected results of a final output's correction units are
172
+ combined: ``'concat'`` (default) concatenates them, ``'average'``
173
+ averages matched volumes (opposite-PE duplicate schemes), and
174
+ ``'none'`` keeps every correction unit as its own output (``None``
175
+ is treated as ``'concat'``).
176
+ use_nipreps_syn_sdc : bool
177
+ Correct series that have no fieldmap with the standalone niworkflows
178
+ SyN-SDC: a constrained ANTs SyN registration of an inverted T1w (or a
179
+ synthetic b=0) to a fieldmap atlas. Never overrides a real fieldmap and
180
+ is never combined with SyNb0 (SyNb0 wins if both are set). Errors if no
181
+ T1w exists or a target series lacks PhaseEncodingDirection.
182
+ b0_threshold : float
183
+ Diffusion-weighting at or below this is treated as b=0 when
184
+ classifying sampling schemes (default:
185
+ :data:`~.metadata.B0_THRESHOLD`).
186
+ strict : bool
187
+ Raise :class:`~.validation.GroupingError` if any error-severity issue
188
+ is found. With ``strict=False`` the grouping is returned with its
189
+ ``issues`` intact, which is what reports and previews want.
190
+
191
+ Returns
192
+ -------
193
+ :class:`~.models.DWIGrouping`
194
+ """
195
+ from bids.layout import parse_file_entities
196
+
197
+ records, index_issues = index_subject(
198
+ layout,
199
+ subject_data,
200
+ ignore_fieldmaps=ignore_fieldmaps,
201
+ b0_threshold=b0_threshold,
202
+ )
203
+ subject_id = parse_file_entities(records[0].path)['subject']
204
+ grouping = build_grouping(
205
+ records,
206
+ subject_id=subject_id,
207
+ separate_all_dwis=separate_all_dwis,
208
+ ignore_fieldmaps=ignore_fieldmaps,
209
+ ignore_shims=ignore_shims,
210
+ ignore_fov=ignore_fov,
211
+ ignore_sdc=ignore_sdc,
212
+ force_t2wreg=force_t2wreg,
213
+ use_synb0=use_synb0,
214
+ use_nipreps_syn_sdc=use_nipreps_syn_sdc,
215
+ distortion_group_merge=distortion_group_merge,
216
+ extra_issues=index_issues,
217
+ )
218
+ if strict:
219
+ raise_for_errors(grouping)
220
+ return grouping
@@ -0,0 +1,8 @@
1
+ """Alias for the ``qsiprep-group`` command line tool (qsiplan.cli)."""
2
+
3
+ import sys
4
+
5
+ from qsiplan.cli import main
6
+
7
+ if __name__ == '__main__':
8
+ sys.exit(main())
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.0'
22
+ __version_tuple__ = version_tuple = (0, 1, 0)
23
+
24
+ __commit_id__ = commit_id = None