Use production command-line workflows for conversion, reconstruction, modeling, and USD export.
+
API Reference
Browse classes and modules for workflows, segmentation, registration, USD, and utilities.
@@ -123,6 +129,14 @@ per-tutorial implementation details.
examples
architecture
+.. toctree::
+ :maxdepth: 2
+ :caption: Isaac for Healthcare
+ :hidden:
+
+ isaac_for_healthcare
+ cli_scripts/byod_tutorials
+
.. toctree::
:maxdepth: 2
:caption: CLI & Scripts Guide
From 0fe660f5fd4e00115ee3d1ac83f1c1b22ab21e23 Mon Sep 17 00:00:00 2001
From: Stephen Aylward
Date: Wed, 20 May 2026 15:26:20 -0400
Subject: [PATCH 3/4] ENH: Clarify how to download demo data
---
AGENTS.md | 47 +++---
docs/API_MAP.md | 11 +-
docs/api/cli/download_data.rst | 10 ++
docs/api/cli/index.rst | 1 +
docs/cli_scripts/byod_tutorials.rst | 213 ++++++++++++++----------
docs/cli_scripts/download_data.rst | 69 ++++++++
docs/cli_scripts/overview.rst | 3 +
docs/index.rst | 17 +-
docs/isaac_for_healthcare.rst | 20 +++
pyproject.toml | 1 +
src/physiomotion4d/cli/__init__.py | 1 +
src/physiomotion4d/cli/download_data.py | 52 ++++++
tests/test_cli_smoke.py | 1 +
tests/test_download_data_cli.py | 50 ++++++
14 files changed, 384 insertions(+), 112 deletions(-)
create mode 100644 docs/api/cli/download_data.rst
create mode 100644 docs/cli_scripts/download_data.rst
create mode 100644 docs/isaac_for_healthcare.rst
create mode 100644 src/physiomotion4d/cli/download_data.py
create mode 100644 tests/test_download_data_cli.py
diff --git a/AGENTS.md b/AGENTS.md
index 821b2c4..293e4dc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -42,43 +42,51 @@ Non-Python tools used by contributor workflows:
## Common Commands
-Use `py` on this Windows system, not `python`.
+Prefer the repository-local virtual environment at `.\venv`. Activate it before
+issuing Python commands so `python`, console scripts, and `uv pip` all use that
+environment. If activation is not possible, invoke
+`.\venv\Scripts\python.exe -m ...` directly. Use `uv run ...` only when the
+local `venv` is unavailable and you need uv to create or sync an environment.
+
+```powershell
+# Create the repo-local environment if it does not already exist
+uv venv venv
+.\venv\Scripts\Activate.ps1
-```bash
# Install in editable mode
uv pip install -e .
# Lint and format
-ruff check . --fix && ruff format .
+python -m ruff check . --fix && python -m ruff format .
# Type checking
-mypy src/ tests/
+python -m mypy src/ tests/
# All pre-commit hooks
-pre-commit run --all-files
+python -m pre_commit run --all-files
# Fast tests
-py -m pytest tests/ -v
+python -m pytest tests/ -v
# Single test file or test by name
-py -m pytest tests/test_contour_tools.py -v
-py -m pytest tests/test_contour_tools.py::test_extract_surface -v
+python -m pytest tests/test_contour_tools.py -v
+python -m pytest tests/test_contour_tools.py::test_extract_surface -v
# Opt-in test buckets
-py -m pytest tests/ -v --run-slow
-py -m pytest tests/ -v --run-gpu
-py -m pytest tests/ -v --run-simpleware
-py -m pytest tests/ -v --run-experiments
-py -m pytest tests/ -v --run-tutorials
+python -m pytest tests/ -v --run-slow
+python -m pytest tests/ -v --run-gpu
+python -m pytest tests/ -v --run-simpleware
+python -m pytest tests/ -v --run-experiments
+python -m pytest tests/ -v --run-tutorials
# Typical local GPU profile
-py -m pytest tests/ -v --run-gpu --run-slow
+python -m pytest tests/ -v --run-gpu --run-slow
# Coverage
-py -m pytest tests/ --cov=src/physiomotion4d --cov-report=html
+python -m pytest tests/ --cov=src/physiomotion4d --cov-report=html
# Create missing baselines
-py -m pytest tests/ --create-baselines
+python -m pytest tests/ --create-baselines
```
Version bumping: `bumpver update --patch`, `--minor`, or `--major`.
@@ -95,7 +103,8 @@ Version bumping: `bumpver update --patch`, `--minor`, or `--major`.
below 88 characters.
- Full type hints are required under strict mypy. Use `Optional[X]`, not
`X | None`.
-- Run `py -m pytest tests/ -v` to verify changes. Slow, GPU, Simpleware,
+- Run `python -m pytest tests/ -v` from the active `.\venv` to verify changes.
+ Slow, GPU, Simpleware,
experiment, and tutorial tests are auto-skipped unless their opt-in flag is
passed.
- The `requires_data` marker no longer exists. Tests that need external data
@@ -172,8 +181,8 @@ Version bumping: `bumpver update --patch`, `--minor`, or `--major`.
- Update docstrings for every changed public method. Keep claims factual.
- Document with docstrings and inline comments.
- Do not create new `.md` files unless explicitly requested.
-- Regenerate `docs/API_MAP.md` after any public API change:
- `py utils/generate_api_map.py`.
+- Regenerate `docs/API_MAP.md` after any public API change from the active
+ `.\venv`: `python utils/generate_api_map.py`.
## Architecture Role
diff --git a/docs/API_MAP.md b/docs/API_MAP.md
index d000788..d434243 100644
--- a/docs/API_MAP.md
+++ b/docs/API_MAP.md
@@ -428,6 +428,10 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
- `def main()` (line 19): Command-line interface for create statistical model workflow.
+## src/physiomotion4d/cli/download_data.py
+
+- `def main(argv=None)` (line 16): Download a supported PhysioMotion4D example dataset.
+
## src/physiomotion4d/cli/fit_statistical_model_to_patient.py
- `def main()` (line 17): Command-line interface for heart model to patient registration.
@@ -870,7 +874,7 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
## tests/test_cli_smoke.py
-- `def test_cli_help(module_name, monkeypatch, capsys)` (line 24): Each CLI module exits successfully for --help.
+- `def test_cli_help(module_name, monkeypatch, capsys)` (line 25): Each CLI module exits successfully for --help.
## tests/test_contour_tools.py
@@ -916,6 +920,11 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
- `def test_mask_ids_missing_boundary_labels_falls_back(self, tmp_path)` (line 562): Mesh without boundary_labels array falls back to a 'default' prim.
- `def test_mask_ids_groups_by_segmenter_type(self, tmp_path)` (line 577): When a segmenter is supplied, labels are grouped under their
+## tests/test_download_data_cli.py
+
+- `def test_download_data_cli_uses_default_dataset_and_directory(monkeypatch, capsys)` (line 14): Default CLI arguments route Slicer-Heart-CT to data/Slicer-Heart-CT.
+- `def test_download_data_cli_uses_requested_directory(monkeypatch, tmp_path)` (line 34): The --directory option controls where Slicer-Heart-CT is stored.
+
## tests/test_download_heart_data.py
- **class TestDataDownloadTools** (line 16): Synthetic tests for dataset verification helpers.
diff --git a/docs/api/cli/download_data.rst b/docs/api/cli/download_data.rst
new file mode 100644
index 0000000..409c1c5
--- /dev/null
+++ b/docs/api/cli/download_data.rst
@@ -0,0 +1,10 @@
+===================
+download_data (CLI)
+===================
+
+.. automodule:: physiomotion4d.cli.download_data
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+See :doc:`../../cli_scripts/download_data` for usage examples.
diff --git a/docs/api/cli/index.rst b/docs/api/cli/index.rst
index 1e57083..b6440d1 100644
--- a/docs/api/cli/index.rst
+++ b/docs/api/cli/index.rst
@@ -22,6 +22,7 @@ Module Index.
convert_image_to_vtk
convert_vtk_to_usd
create_statistical_model
+ download_data
fit_statistical_model_to_patient
reconstruct_highres_4d_ct
visualize_pca_modes
diff --git a/docs/cli_scripts/byod_tutorials.rst b/docs/cli_scripts/byod_tutorials.rst
index e14f846..fed656a 100644
--- a/docs/cli_scripts/byod_tutorials.rst
+++ b/docs/cli_scripts/byod_tutorials.rst
@@ -1,19 +1,18 @@
.. _byod_tutorials:
-Bring Your Own Data — DICOM & VTK to USD
-=========================================
+Bring Your Own Data - DICOM, Images & VTK to USD
+================================================
-PhysioMotion4D lets you convert your own medical imaging data — whether
-DICOM-derived NIfTI volumes or VTK surface meshes — into OpenUSD for
-interactive visualization in NVIDIA Omniverse. Both 3D (single
-volume/mesh) and 4D (time-series) inputs are supported. The CLI and
-Python API are **identical** for 3D and 4D inputs; the only difference
-is how many files you pass in.
+PhysioMotion4D lets you convert your own medical imaging data into OpenUSD for
+interactive visualization in NVIDIA Omniverse. Image inputs may be a directory
+of 3D or 4D DICOM data, a single 3D or 4D file in a common medical image format
+such as MHA, NRRD, or NIfTI, or a list of 3D image files representing a time
+series. VTK inputs may be one mesh file or a mesh sequence.
.. note::
PhysioMotion4D is a research tool and has **not** been validated for
- clinical use. Outputs must not be used for diagnostic or therapeutic
+ clinical use. Outputs must not be used for diagnostic or therapeutic
decisions without independent validation.
Installation
@@ -24,7 +23,7 @@ or the CPU-only variant:
.. code-block:: bash
- # Recommended — CUDA-enabled
+ # Recommended - CUDA-enabled
pip install physiomotion4d[cuda13]
# CPU-only
@@ -34,31 +33,56 @@ Verify that both relevant CLI entry-points are available after installation:
.. code-block:: bash
+ physiomotion4d-download-data --help
physiomotion4d-convert-image-to-usd --help
physiomotion4d-convert-vtk-to-usd --help
See :doc:`/installation` for prerequisites, CUDA version requirements, and
source-based installation.
-DICOM to USD
-------------
+Download Demonstration Data
+---------------------------
+
+Use the installed download CLI to fetch the public Slicer-Heart 4D CT sample:
+
+.. code-block:: bash
+
+ physiomotion4d-download-data
-Raw DICOM images must first be converted to NIfTI with a tool such as
-`dcm2niix `_ before being passed
-to PhysioMotion4D.
+This stores ``TruncalValve_4DCT.seq.nrrd`` under
+``data/Slicer-Heart-CT``. To choose a different location:
-3D — Single Volume
-~~~~~~~~~~~~~~~~~~
+.. code-block:: bash
-Pass a single ``.nii.gz`` file to produce a static USD scene.
+ physiomotion4d-download-data Slicer-Heart-CT \
+ --directory path/to/Slicer-Heart-CT
+
+DICOM and Medical Images to USD
+-------------------------------
+
+Image-to-USD conversion accepts DICOM directories directly. It also accepts
+3D and 4D image files readable by ITK, including common formats such as
+``.mha``, ``.nrrd``, ``.nii``, and ``.nii.gz``.
+
+3D - Single DICOM Directory or Image File
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pass a DICOM series directory or a single 3D image file to produce a static USD
+scene.
**CLI:**
.. code-block:: bash
physiomotion4d-convert-image-to-usd \
- patient_ct.nii.gz \
- --output patient_heart.usd
+ patient_dicom_dir \
+ --output-dir ./results \
+ --project-name patient_heart
+
+ physiomotion4d-convert-image-to-usd \
+ patient_ct.mha \
+ --output-dir ./results \
+ --project-name patient_heart
**Python API:**
@@ -66,71 +90,83 @@ Pass a single ``.nii.gz`` file to produce a static USD scene.
import physiomotion4d as pm4d
- wf = pm4d.WorkflowConvertImageToUSD()
- wf.input_image = "patient_ct.nii.gz"
- wf.output_file = "patient_heart.usd"
- wf.run_workflow()
-
-.. note::
+ workflow = pm4d.WorkflowConvertImageToUSD(
+ input_filenames=["patient_dicom_dir"],
+ contrast_enhanced=False,
+ output_directory="./results",
+ project_name="patient_heart",
+ )
+ workflow.process()
- If your source data is raw DICOM, run ``dcm2niix -z y -o output_dir
- dicom_dir/`` first to produce the ``.nii.gz`` input file expected by
- this command.
+The workflow writes ``.dynamic_painted.usd``,
+``.static_painted.usd``, and ``.all_painted.usd``
+inside ``--output-dir``.
-4D — Gated CT Time Series
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+4D - DICOM Directory, 4D Image File, or 3D Image List
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Pass multiple per-phase volumes (glob or explicit list) to produce an
-animated USD scene. Use ``--fps`` to control playback rate and
-``--reference-frame`` to choose the registration anchor phase (0-indexed).
+Pass a 4D DICOM directory, a single 4D image file, or an explicit list of 3D
+image files to produce an animated USD scene. The image-to-USD CLI does not
+provide an ``--fps`` option. Use ``--reference-image`` only when you need to
+provide a separate fixed image for registration; otherwise the workflow selects
+its default reference frame internally.
**CLI:**
.. code-block:: bash
physiomotion4d-convert-image-to-usd \
- phase_*.nii.gz \
- --output heart_animated.usd \
- --fps 25 \
- --reference-frame 0
+ gated_ct_dicom_dir \
+ --output-dir ./results \
+ --project-name heart_animated
+
+ physiomotion4d-convert-image-to-usd \
+ gated_ct_4d.nrrd \
+ --output-dir ./results \
+ --project-name heart_animated
+
+ physiomotion4d-convert-image-to-usd \
+ phase_000.mha phase_001.mha phase_002.mha \
+ --output-dir ./results \
+ --project-name heart_animated
**Python API:**
.. code-block:: python
- import glob
import physiomotion4d as pm4d
- wf = pm4d.WorkflowConvertImageToUSD()
- wf.input_images = sorted(glob.glob("phase_*.nii.gz"))
- wf.output_file = "heart_animated.usd"
- wf.fps = 25
- wf.reference_frame = 0
- wf.run_workflow()
+ workflow = pm4d.WorkflowConvertImageToUSD(
+ input_filenames=["phase_000.mha", "phase_001.mha", "phase_002.mha"],
+ contrast_enhanced=False,
+ output_directory="./results",
+ project_name="heart_animated",
+ )
+ workflow.process()
-The resulting USD file contains a time-sampled mesh sequence that plays
-back when you press **Play** in Omniverse USD Composer.
+The resulting USD file contains a time-sampled mesh sequence that plays back
+when you press **Play** in Omniverse USD Composer.
VTK to USD
----------
-3D — Single Mesh
+3D - Single Mesh
~~~~~~~~~~~~~~~~
-Pass a single ``.vtp`` file. Use ``--appearance`` to control material
-style and ``--no-split`` to skip the default connected-component split.
+Pass a single ``.vtp`` file. Use ``--appearance`` to control material style and
+``--no-split`` to skip the default connected-component split.
**CLI:**
.. code-block:: bash
- # Default — split by connected component, anatomy material
+ # Default - split by connected component, anatomy material
physiomotion4d-convert-vtk-to-usd heart.vtp \
--output heart.usd \
--appearance anatomy \
--anatomy-type heart
- # Solid colour, no splitting
+ # Solid color, no splitting
physiomotion4d-convert-vtk-to-usd mesh.vtp \
--output mesh_red.usd \
--appearance solid \
@@ -143,18 +179,21 @@ style and ``--no-split`` to skip the default connected-component split.
import physiomotion4d as pm4d
- wf = pm4d.WorkflowConvertVTKToUSD()
- wf.input_files = ["heart.vtp"]
- wf.output_file = "heart.usd"
- wf.appearance = "anatomy"
- wf.anatomy_type = "heart"
- wf.run()
+ workflow = pm4d.WorkflowConvertVTKToUSD(
+ vtk_files=["heart.vtp"],
+ output_usd="heart.usd",
+ appearance="anatomy",
+ anatomy_type="heart",
+ )
+ workflow.run()
-4D — Mesh Time Series
+4D - Mesh Time Series
~~~~~~~~~~~~~~~~~~~~~
-Pass a glob of per-frame files. The ``--fps`` flag controls playback
-rate. For scalar colormaps, combine ``--primvar``, ``--cmap``, and
+Pass per-frame VTK files. The default workflow treats multiple files as a time
+series when their names match ``.t.vtk``, ``.t.vtp``, or
+``.t.vtu``. The VTK-to-USD CLI supports ``--fps`` to control playback
+rate. For scalar colormaps, combine ``--primvar``, ``--cmap``, and
``--intensity-range``.
**CLI:**
@@ -162,12 +201,12 @@ rate. For scalar colormaps, combine ``--primvar``, ``--cmap``, and
.. code-block:: bash
# Animated mesh sequence
- physiomotion4d-convert-vtk-to-usd frame_*.vtp \
+ physiomotion4d-convert-vtk-to-usd heart.t0.vtp heart.t1.vtp heart.t2.vtp \
--output heart_animation.usd \
--fps 30
# Animated with scalar colormap (e.g. wall stress)
- physiomotion4d-convert-vtk-to-usd frame_*.vtk \
+ physiomotion4d-convert-vtk-to-usd stress.t0.vtk stress.t1.vtk stress.t2.vtk \
--output stress_animation.usd \
--fps 30 \
--appearance colormap \
@@ -179,18 +218,18 @@ rate. For scalar colormaps, combine ``--primvar``, ``--cmap``, and
.. code-block:: python
- import glob
import physiomotion4d as pm4d
- wf = pm4d.WorkflowConvertVTKToUSD()
- wf.input_files = sorted(glob.glob("frame_*.vtk"))
- wf.output_file = "stress_animation.usd"
- wf.fps = 30
- wf.appearance = "colormap"
- wf.primvar = "vtk_point_stress_c0"
- wf.cmap = "viridis"
- wf.intensity_range = (0, 500)
- wf.run()
+ workflow = pm4d.WorkflowConvertVTKToUSD(
+ vtk_files=["stress.t0.vtk", "stress.t1.vtk", "stress.t2.vtk"],
+ output_usd="stress_animation.usd",
+ times_per_second=30,
+ appearance="colormap",
+ colormap_primvar="vtk_point_stress_c0",
+ colormap_name="viridis",
+ colormap_intensity_range=(0, 500),
+ )
+ workflow.run()
**Lower-level in-memory conversion with ConvertVTKToUSD:**
@@ -205,28 +244,34 @@ lower-level :class:`physiomotion4d.ConvertVTKToUSD` class directly:
# Load or construct meshes in memory
meshes = [pv.read(f"frame_{i:04d}.vtp") for i in range(10)]
- converter = pm4d.ConvertVTKToUSD(output_file="output.usd", fps=30)
- for i, mesh in enumerate(meshes):
- converter.add_frame(mesh, frame_index=i)
- converter.write()
+ converter = pm4d.ConvertVTKToUSD(
+ data_basename="HeartAnimation",
+ input_polydata=meshes,
+ times_per_second=30,
+ )
+ converter.convert("output.usd")
Viewing Results
---------------
-**Quick preview with PyVista (no Omniverse required):**
+**Programmatic inspection:**
.. code-block:: python
- import pyvista as pv
+ import physiomotion4d as pm4d
+
+ mesh = pm4d.USDTools().load_usd_as_vtk("output.usd")
+ print(mesh.n_points, mesh.n_cells)
- mesh = pv.read("output.usd")
- mesh.plot()
+PyVista reads the VTK input files used above, but local validation with
+PyVista 0.48.4 shows that ``pyvista.read()`` / ``pyvista.get_reader()`` do not
+support ``.usd``, ``.usda``, or ``.usdc`` output files directly.
**In NVIDIA Omniverse:**
-Open **Omniverse USD Composer**, drag your ``.usd`` file onto the
-viewport, then press **Play** (spacebar) to watch the animation. For
-4D cardiac data, use the **Timeline** panel to scrub through phases.
+Open **Omniverse USD Composer**, drag your ``.usd`` file onto the viewport,
+then press **Play** (spacebar) to watch the animation. For 4D cardiac data,
+use the **Timeline** panel to scrub through phases.
See Also
--------
diff --git a/docs/cli_scripts/download_data.rst b/docs/cli_scripts/download_data.rst
new file mode 100644
index 0000000..dc7f800
--- /dev/null
+++ b/docs/cli_scripts/download_data.rst
@@ -0,0 +1,69 @@
+=====================
+Download Example Data
+=====================
+
+The ``physiomotion4d-download-data`` command downloads example datasets used by
+PhysioMotion4D tutorials and demos.
+
+Supported Datasets
+==================
+
+.. list-table::
+ :widths: 35 65
+ :header-rows: 1
+
+ * - Data name
+ - Description
+ * - ``Slicer-Heart-CT``
+ - Public 4D cardiac CT sample from SlicerHeart. This is currently the
+ only dataset downloaded automatically by PhysioMotion4D.
+
+Basic Usage
+===========
+
+Download the default dataset into the default location:
+
+.. code-block:: bash
+
+ physiomotion4d-download-data
+
+This is equivalent to:
+
+.. code-block:: bash
+
+ physiomotion4d-download-data Slicer-Heart-CT \
+ --directory data/Slicer-Heart-CT
+
+Options
+=======
+
+.. code-block:: bash
+
+ physiomotion4d-download-data [Slicer-Heart-CT] [--directory DIRECTORY]
+
+``data_name``
+ Dataset to download. The only accepted value is ``Slicer-Heart-CT``.
+
+``--directory``
+ Directory where the dataset is stored. Defaults to
+ ``data/Slicer-Heart-CT``.
+
+Output
+======
+
+For ``Slicer-Heart-CT``, the command downloads or reuses:
+
+.. code-block:: text
+
+ data/Slicer-Heart-CT/TruncalValve_4DCT.seq.nrrd
+
+The command uses
+:meth:`physiomotion4d.data_download_tools.DataDownloadTools.DownloadSlicerHeartCTData`,
+so repeated runs reuse the existing non-empty file.
+
+See Also
+========
+
+* :doc:`byod_tutorials`
+* :doc:`heart_gated_ct`
+* :doc:`overview`
diff --git a/docs/cli_scripts/overview.rst b/docs/cli_scripts/overview.rst
index 2ed59a4..75d0a8d 100644
--- a/docs/cli_scripts/overview.rst
+++ b/docs/cli_scripts/overview.rst
@@ -49,6 +49,8 @@ Current Scripts
* - Script
- Description
+ * - :doc:`download_data`
+ - Download supported PhysioMotion4D example datasets
* - :doc:`heart_gated_ct`
- Process cardiac gated CT to animated heart models with physiological motion
* - ``physiomotion4d-convert-image-to-vtk``
@@ -80,6 +82,7 @@ After installation, scripts are available as command-line tools with the prefix
.. code-block:: bash
physiomotion4d-convert-image-to-usd --help
+ physiomotion4d-download-data --help
General Workflow
================
diff --git a/docs/index.rst b/docs/index.rst
index 801095c..c3651df 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -129,20 +129,13 @@ per-tutorial implementation details.
examples
architecture
-.. toctree::
- :maxdepth: 2
- :caption: Isaac for Healthcare
- :hidden:
-
- isaac_for_healthcare
- cli_scripts/byod_tutorials
-
.. toctree::
:maxdepth: 2
:caption: CLI & Scripts Guide
:hidden:
cli_scripts/overview
+ cli_scripts/download_data
cli_scripts/heart_gated_ct
cli_scripts/create_statistical_model
cli_scripts/fit_statistical_model_to_patient
@@ -180,6 +173,14 @@ per-tutorial implementation details.
contributing
testing
+.. toctree::
+ :maxdepth: 2
+ :caption: Isaac for Healthcare
+ :hidden:
+
+ isaac_for_healthcare
+ cli_scripts/byod_tutorials
+
.. toctree::
:maxdepth: 1
:caption: Additional Resources
diff --git a/docs/isaac_for_healthcare.rst b/docs/isaac_for_healthcare.rst
new file mode 100644
index 0000000..b860a96
--- /dev/null
+++ b/docs/isaac_for_healthcare.rst
@@ -0,0 +1,20 @@
+Isaac for Healthcare
+====================
+
+Use these resources to create, inspect, and download PhysioMotion4D assets for
+Isaac for Healthcare workflows.
+
+.. raw:: html
+
+
diff --git a/pyproject.toml b/pyproject.toml
index aa79a2f..1131183 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -165,6 +165,7 @@ physiomotion4d-convert-image-4d-to-3d = "physiomotion4d.cli.convert_image_4d_to_
physiomotion4d-convert-image-to-usd = "physiomotion4d.cli.convert_image_to_usd:main"
physiomotion4d-convert-vtk-to-usd = "physiomotion4d.cli.convert_vtk_to_usd:main"
physiomotion4d-create-statistical-model = "physiomotion4d.cli.create_statistical_model:main"
+physiomotion4d-download-data = "physiomotion4d.cli.download_data:main"
physiomotion4d-fit-statistical-model-to-patient = "physiomotion4d.cli.fit_statistical_model_to_patient:main"
physiomotion4d-reconstruct-highres-4d-ct = "physiomotion4d.cli.reconstruct_highres_4d_ct:main"
physiomotion4d-visualize-pca-modes = "physiomotion4d.cli.visualize_pca_modes:main"
diff --git a/src/physiomotion4d/cli/__init__.py b/src/physiomotion4d/cli/__init__.py
index ca3f06d..d498484 100644
--- a/src/physiomotion4d/cli/__init__.py
+++ b/src/physiomotion4d/cli/__init__.py
@@ -5,6 +5,7 @@
"convert_image_4d_to_3d",
"convert_vtk_to_usd",
"create_statistical_model",
+ "download_data",
"fit_statistical_model_to_patient",
"visualize_pca_modes",
]
diff --git a/src/physiomotion4d/cli/download_data.py b/src/physiomotion4d/cli/download_data.py
new file mode 100644
index 0000000..eaf9399
--- /dev/null
+++ b/src/physiomotion4d/cli/download_data.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+"""Command-line interface for downloading PhysioMotion4D example data."""
+
+from __future__ import annotations
+
+import argparse
+import sys
+from pathlib import Path
+from typing import Optional
+
+from physiomotion4d.data_download_tools import DataDownloadTools
+
+SLICER_HEART_CT = "Slicer-Heart-CT"
+
+
+def main(argv: Optional[list[str]] = None) -> int:
+ """Download a supported PhysioMotion4D example dataset."""
+ parser = argparse.ArgumentParser(
+ description="Download PhysioMotion4D example data",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog=f"""
+Examples:
+ %(prog)s
+ %(prog)s {SLICER_HEART_CT} --directory data/Slicer-Heart-CT
+ """,
+ )
+ parser.add_argument(
+ "data_name",
+ nargs="?",
+ choices=[SLICER_HEART_CT],
+ default=SLICER_HEART_CT,
+ help=f"Dataset to download (default: {SLICER_HEART_CT})",
+ )
+ parser.add_argument(
+ "--directory",
+ default=f"data/{SLICER_HEART_CT}",
+ help=f"Directory where data will be stored (default: data/{SLICER_HEART_CT})",
+ )
+
+ args = parser.parse_args(argv)
+ output_dir = Path(args.directory)
+
+ if args.data_name == SLICER_HEART_CT:
+ data_file = DataDownloadTools.DownloadSlicerHeartCTData(output_dir)
+ print(f"Downloaded {SLICER_HEART_CT} to: {data_file}")
+ return 0
+
+ parser.error(f"Unsupported dataset: {args.data_name}")
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py
index dcb236a..5b49fd5 100644
--- a/tests/test_cli_smoke.py
+++ b/tests/test_cli_smoke.py
@@ -14,6 +14,7 @@
"physiomotion4d.cli.convert_image_to_usd",
"physiomotion4d.cli.convert_vtk_to_usd",
"physiomotion4d.cli.create_statistical_model",
+ "physiomotion4d.cli.download_data",
"physiomotion4d.cli.fit_statistical_model_to_patient",
"physiomotion4d.cli.reconstruct_highres_4d_ct",
"physiomotion4d.cli.visualize_pca_modes",
diff --git a/tests/test_download_data_cli.py b/tests/test_download_data_cli.py
new file mode 100644
index 0000000..df6c567
--- /dev/null
+++ b/tests/test_download_data_cli.py
@@ -0,0 +1,50 @@
+"""Tests for the dataset download CLI wrapper."""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Union
+
+import pytest
+
+from physiomotion4d.cli import download_data
+from physiomotion4d.data_download_tools import DataDownloadTools
+
+
+def test_download_data_cli_uses_default_dataset_and_directory(
+ monkeypatch: pytest.MonkeyPatch,
+ capsys: pytest.CaptureFixture[str],
+) -> None:
+ """Default CLI arguments route Slicer-Heart-CT to data/Slicer-Heart-CT."""
+ calls: list[Path] = []
+
+ def fake_download(dirname: Union[str, Path]) -> Path:
+ calls.append(Path(dirname))
+ return Path(dirname) / DataDownloadTools.SLICER_HEART_CT_FILENAME
+
+ monkeypatch.setattr(DataDownloadTools, "DownloadSlicerHeartCTData", fake_download)
+
+ result = download_data.main([])
+
+ assert result == 0
+ assert calls == [Path("data/Slicer-Heart-CT")]
+ assert "Downloaded Slicer-Heart-CT" in capsys.readouterr().out
+
+
+def test_download_data_cli_uses_requested_directory(
+ monkeypatch: pytest.MonkeyPatch,
+ tmp_path: Path,
+) -> None:
+ """The --directory option controls where Slicer-Heart-CT is stored."""
+ calls: list[Path] = []
+
+ def fake_download(dirname: Union[str, Path]) -> Path:
+ calls.append(Path(dirname))
+ return Path(dirname) / DataDownloadTools.SLICER_HEART_CT_FILENAME
+
+ monkeypatch.setattr(DataDownloadTools, "DownloadSlicerHeartCTData", fake_download)
+
+ result = download_data.main(["Slicer-Heart-CT", "--directory", str(tmp_path)])
+
+ assert result == 0
+ assert calls == [tmp_path]
From c44c91abe72296973a684b561665bfc2deaf7ea7 Mon Sep 17 00:00:00 2001
From: Stephen Aylward
Date: Wed, 20 May 2026 17:15:03 -0400
Subject: [PATCH 4/4] ENH: Updated docs to use variable for version number
---
docs/architecture.rst | 4 ++--
docs/cli_scripts/best_practices.rst | 2 +-
docs/cli_scripts/byod_tutorials.rst | 2 +-
docs/contributing.rst | 2 +-
docs/index.rst | 6 +++---
docs/installation.rst | 2 +-
docs/quickstart.rst | 7 ++++---
7 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/docs/architecture.rst b/docs/architecture.rst
index 7524cd8..58d09aa 100644
--- a/docs/architecture.rst
+++ b/docs/architecture.rst
@@ -9,8 +9,8 @@ configuration.
.. warning::
- PhysioMotion4D 2026.05.07 beta is not validated for clinical use. It is a
- research and visualization toolkit, not a medical device.
+ PhysioMotion4D {{ pm4d_project_version }} beta is not validated for clinical
+ use. It is a research and visualization toolkit, not a medical device.
Data Flow
=========
diff --git a/docs/cli_scripts/best_practices.rst b/docs/cli_scripts/best_practices.rst
index 532839e..2bd2a4c 100644
--- a/docs/cli_scripts/best_practices.rst
+++ b/docs/cli_scripts/best_practices.rst
@@ -242,7 +242,7 @@ Ensuring Reproducible Results
# processing_metadata.yaml
patient_id: patient_001
- physiomotion4d_version: 2026.05.07
+ physiomotion4d_version: {{ pm4d_project_version }}
script: heart-gated-ct
date: 2026-01-08
diff --git a/docs/cli_scripts/byod_tutorials.rst b/docs/cli_scripts/byod_tutorials.rst
index fed656a..ec43132 100644
--- a/docs/cli_scripts/byod_tutorials.rst
+++ b/docs/cli_scripts/byod_tutorials.rst
@@ -29,7 +29,7 @@ or the CPU-only variant:
# CPU-only
pip install physiomotion4d
-Verify that both relevant CLI entry-points are available after installation:
+Verify that all three relevant CLI entry-points are available after installation:
.. code-block:: bash
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 95854c6..a37d0c2 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -410,7 +410,7 @@ PhysioMotion4D uses calendar versioning: ``YYYY.0M.PATCH``
* **0M**: Zero-padded month
* **PATCH**: Patch number within month
-Example: ``2026.05.07``
+Example: ``{{ pm4d_project_version }}``
Making a Release
----------------
diff --git a/docs/index.rst b/docs/index.rst
index c3651df..458de99 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -194,9 +194,9 @@ per-tutorial implementation details.
Clinical Use
============
-Not validated for clinical use. PhysioMotion4D 2026.05.07 beta is a research
-and visualization toolkit, not a medical device. Do not use it for diagnosis,
-treatment planning, or clinical decision-making.
+Not validated for clinical use. PhysioMotion4D {{ pm4d_project_version }} beta
+is a research and visualization toolkit, not a medical device. Do not use it
+for diagnosis, treatment planning, or clinical decision-making.
Indices and tables
==================
diff --git a/docs/installation.rst b/docs/installation.rst
index 8fa03d8..f63ce96 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -171,7 +171,7 @@ Expected output:
.. code-block:: text
- PhysioMotion4D version: 2026.05.07
+ PhysioMotion4D version: {{ pm4d_project_version }}
WorkflowConvertImageToUSD
Command-Line Tools
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index d719f87..182a26c 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -6,9 +6,10 @@ This guide will help you get started with PhysioMotion4D quickly.
.. warning::
- **Not validated for clinical use.** PhysioMotion4D 2026.05.07 beta is a
- research and visualization toolkit, not a medical device. Do not use it for
- diagnosis, treatment planning, or clinical decision-making.
+ **Not validated for clinical use.** PhysioMotion4D
+ {{ pm4d_project_version }} beta is a research and visualization toolkit, not
+ a medical device. Do not use it for diagnosis, treatment planning, or
+ clinical decision-making.
.. _tutorials: