Skip to content

ENH: Simplified naming of tutorials and updated their dirs#89

Open
aylward wants to merge 4 commits into
Project-MONAI:mainfrom
aylward:tutorials_rename
Open

ENH: Simplified naming of tutorials and updated their dirs#89
aylward wants to merge 4 commits into
Project-MONAI:mainfrom
aylward:tutorials_rename

Conversation

@aylward

@aylward aylward commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added a new Tutorial 3 for converting heart VTK surfaces to USD.
    • Added a bring-your-own-data workflow (Tutorial 8) to fit a cardiac statistical model to patient reference data and propagate it across gated phases.
  • Improvements

    • Updated tutorial naming, output locations, and recommended run order for Tutorials 1–10, including BYOD training/evaluation splits and paths.
    • Enhanced registration configurability in the high-res 4D CT reconstruction CLI.
    • Refined the optional PhysicsNeMo installation to include torch-scatter>=2.1.0.
  • Tests

    • Updated the end-to-end and workflow reconstruction tests to match the renamed tutorials and new output layout.

Copilot AI review requested due to automatic review settings July 15, 2026 11:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Walkthrough

Changes

Tutorial workflow updates

Layer / File(s) Summary
Core tutorial workflows
tutorials/tutorial_01_*, tutorials/tutorial_02_*, tutorials/tutorial_03_*, tutorials/tutorial_04_*, tutorials/tutorial_05_*, tutorials/tutorial_06_*
Tutorial scripts now use consolidated names and output paths, explicitly configure segmentation where required, add heart VTK-to-USD and statistical-model workflows, and update downstream PCA artifact paths.
Reconstruction and USD workflow APIs
src/physiotwin4d/workflow_reconstruct_highres_4d_ct.py, src/physiotwin4d/workflow_convert_image_to_usd.py, src/physiotwin4d/cli/reconstruct_highres_4d_ct.py
Reconstruction parameters use reference-image terminology and configurable reference registration; USD registration uses a unified helper and revised dynamic-mask handling.
Tutorial validation and documentation
tests/test_tutorials.py, tests/test_workflow_reconstruct_highres_4d_ct.py, docs/*, tutorials/README.md
Tests and documentation reference the consolidated tutorial scripts, output directories, anchors, run order, and workflow constructor names.

BYOD statistical-model propagation

Layer / File(s) Summary
Reference fitting and gated-phase propagation
tutorials/tutorial_08_byod_fit_model_to_patients.py
Adds a standalone pipeline that fits a cardiac statistical model, registers gated phases, transforms fitted surfaces, supports recomputation or cached artifacts, and records per-patient results.

PhysicsNeMo BYOD training and evaluation

Layer / File(s) Summary
Dependency and type-check configuration
pyproject.toml
Adds torch-scatter>=2.1.0, installation notes, and updated tutorial module override names.
Training and evaluation configuration
tutorials/tutorial_09_byod_train_physicsnemo_*.py, tutorials/tutorial_10_byod_eval_physicsnemo_*.py
Renames model output paths, changes the held-out subject to pm0027, clears validation subjects, and updates evaluation defaults.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PatientDataset
  participant WorkflowFitStatisticalModelToPatient
  participant WorkflowReconstructHighres4DCT
  participant TransformTools
  PatientDataset->>WorkflowFitStatisticalModelToPatient: provide reference image and labelmap
  WorkflowFitStatisticalModelToPatient-->>TransformTools: fitted SSM mesh and surface
  PatientDataset->>WorkflowReconstructHighres4DCT: provide gated phase images
  WorkflowReconstructHighres4DCT-->>TransformTools: phase transforms
  TransformTools-->>PatientDataset: per-phase mesh and surface outputs
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: simplified tutorial names and updated output directories.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tutorials/tutorial_08_byod_fit_model_to_patients.py`:
- Around line 2-10: Update the tutorial title and downstream references in the
module documentation to use the renamed tutorial_08_* and tutorial_09_*
identifiers instead of the stale 08cd, 09c, and 09d names; apply the same
correction to the additional referenced documentation section while keeping the
pipeline description factual.
- Around line 194-215: Update the workflow construction in the RECOMPUTE block
to configure WorkflowReconstructHighres4DCT so the first gated phase is
registered to ref_image rather than treated as the identity frame. Preserve the
existing time_series and registration method setup while overriding the default
reference-frame behavior with the appropriate register_reference configuration.

In `@tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py`:
- Around line 43-51: Update tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py
lines 43-51: remove the “9c” reference from the run-directory comment and change
DEFAULT_OUT_DIR to use the tutorial_10_byod_mgn directory. Update
tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py lines 43-51 by removing the
stale “9d” reference from its corresponding comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62fc732b-d008-4e7e-a72f-a3bc51d0dcd1

📥 Commits

Reviewing files that changed from the base of the PR and between 34f3f97 and 02f1d90.

📒 Files selected for processing (13)
  • pyproject.toml
  • tutorials/tutorial_01_heart_gated_ct_to_usd.py
  • tutorials/tutorial_01_lung_gated_ct_to_usd.py
  • tutorials/tutorial_02_heart_ct_to_vtk.py
  • tutorials/tutorial_03_heart_vtk_to_usd.py
  • tutorials/tutorial_04_heart_create_statistical_model.py
  • tutorials/tutorial_05_heart_to_lung_fit_statistical_model_to_patient.py
  • tutorials/tutorial_06_lung_reconstruct_highres_4d_ct.py
  • tutorials/tutorial_08_byod_fit_model_to_patients.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mgn.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mlp.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tutorials/tutorial_08_byod_fit_model_to_patients.py`:
- Around line 2-10: Update the tutorial title and downstream references in the
module documentation to use the renamed tutorial_08_* and tutorial_09_*
identifiers instead of the stale 08cd, 09c, and 09d names; apply the same
correction to the additional referenced documentation section while keeping the
pipeline description factual.
- Around line 194-215: Update the workflow construction in the RECOMPUTE block
to configure WorkflowReconstructHighres4DCT so the first gated phase is
registered to ref_image rather than treated as the identity frame. Preserve the
existing time_series and registration method setup while overriding the default
reference-frame behavior with the appropriate register_reference configuration.

In `@tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py`:
- Around line 43-51: Update tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py
lines 43-51: remove the “9c” reference from the run-directory comment and change
DEFAULT_OUT_DIR to use the tutorial_10_byod_mgn directory. Update
tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py lines 43-51 by removing the
stale “9d” reference from its corresponding comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62fc732b-d008-4e7e-a72f-a3bc51d0dcd1

📥 Commits

Reviewing files that changed from the base of the PR and between 34f3f97 and 02f1d90.

📒 Files selected for processing (13)
  • pyproject.toml
  • tutorials/tutorial_01_heart_gated_ct_to_usd.py
  • tutorials/tutorial_01_lung_gated_ct_to_usd.py
  • tutorials/tutorial_02_heart_ct_to_vtk.py
  • tutorials/tutorial_03_heart_vtk_to_usd.py
  • tutorials/tutorial_04_heart_create_statistical_model.py
  • tutorials/tutorial_05_heart_to_lung_fit_statistical_model_to_patient.py
  • tutorials/tutorial_06_lung_reconstruct_highres_4d_ct.py
  • tutorials/tutorial_08_byod_fit_model_to_patients.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mgn.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mlp.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py
🛑 Comments failed to post (3)
tutorials/tutorial_08_byod_fit_model_to_patients.py (2)

2-10: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale tutorial names.

The title and downstream references still use the old 08cd/09c/09d naming, while this PR renames them to tutorial_08_* and tutorial_09_*.

Proposed documentation update
-Tutorial 8cd: Fit the Cardiac SSM and Propagate It Through Gated Phases
+Tutorial 08: Fit the Cardiac SSM and Propagate It Through Gated Phases

-First stage of the cardiac 4D deep-learning pipeline (Tutorials 08cd -> 09c/09d
--> 10c/10d).
+First stage of the cardiac 4D deep-learning pipeline
+(Tutorials 08 -> 09 -> 10).

-``tutorial_09c_byod_train_physicsnemo_mgn.py`` /
-``tutorial_09d_byod_train_physicsnemo_mlp.py``)
+``tutorial_09_byod_train_physicsnemo_mgn.py`` /
+``tutorial_09_byod_train_physicsnemo_mlp.py``)

-# this is also the directory the Tutorial 9c/9d trainers read from.
+# this is also the directory the Tutorial 09 trainers read from.

As per coding guidelines, keep documentation claims factual.

Also applies to: 85-86

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/tutorial_08_byod_fit_model_to_patients.py` around lines 2 - 10,
Update the tutorial title and downstream references in the module documentation
to use the renamed tutorial_08_* and tutorial_09_* identifiers instead of the
stale 08cd, 09c, and 09d names; apply the same correction to the additional
referenced documentation section while keeping the pipeline description factual.

Source: Coding guidelines


194-215: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Register the first gated image instead of treating it as identity.

The reference image is excluded from time_series, but the workflow defaults to
reference_frame=0, register_reference=False. Consequently, the first gated phase
receives an identity transform rather than registration to ref_image.

Proposed fix
             reg_workflow = WorkflowReconstructHighres4DCT(
                 time_series_images=time_series,
                 fixed_image=ref_image,
+                register_reference=True,
                 registration_method=icon_registration_method,
             )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        gated_files = sorted(
            file
            for file in patient_dir.glob("*.nii.gz")
            if file != ref_image_file and "nop" not in file.name and "_g" in file.stem
        )

        time_series = []
        time_series_ids = []
        for gated_file in gated_files:
            time_series.append(itk.imread(str(gated_file)))
            time_id = gated_file.name.split("_g")[1][:3]
            time_series_ids.append(time_id)

        if RECOMPUTE:
            icon_registration_method = RegisterImagesICON()
            icon_registration_method.set_weights_path(str(ICON_WEIGHTS_PATH))
            icon_registration_method.set_number_of_iterations(None)
            reg_workflow = WorkflowReconstructHighres4DCT(
                time_series_images=time_series,
                fixed_image=ref_image,
                register_reference=True,
                registration_method=icon_registration_method,
            )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/tutorial_08_byod_fit_model_to_patients.py` around lines 194 - 215,
Update the workflow construction in the RECOMPUTE block to configure
WorkflowReconstructHighres4DCT so the first gated phase is registered to
ref_image rather than treated as the identity frame. Preserve the existing
time_series and registration method setup while overriding the default
reference-frame behavior with the appropriate register_reference configuration.
tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py (1)

43-51: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale tutorial references and standardize output directory naming.

The PR successfully simplified the tutorial names and output paths, but these comments still reference the old 9c/9d numbering. Additionally, the MGN script's default output directory should include the _byod_ prefix for consistency with the MLP script (tutorial_10_byod_mlp).

  • tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py#L43-L51: Update the comment to remove "9c", and standardize DEFAULT_OUT_DIR to use tutorial_10_byod_mgn.
  • tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py#L43-L51: Update the comment to remove "9d".
📍 Affects 2 files
  • tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py#L43-L51 (this comment)
  • tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py#L43-L51
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py` around lines 43 - 51,
Update tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py lines 43-51: remove
the “9c” reference from the run-directory comment and change DEFAULT_OUT_DIR to
use the tutorial_10_byod_mgn directory. Update
tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py lines 43-51 by removing the
stale “9d” reference from its corresponding comment.

Copilot AI review requested due to automatic review settings July 15, 2026 13:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 15, 2026 14:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 15, 2026 14:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/quickstart.rst (1)

276-279: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the outdated tutorial numbering.

The 9a/9b/10a/10b variant suffixes were removed in this PR. Please update this reference to match the consolidated tutorials to avoid confusing users.

📝 Proposed fix
 bring-your-own-data cardiac tutorials; see :doc:`tutorials` for their dataset
-layout. Tutorials 9a/9b/10a/10b additionally require the optional
+layout. Tutorials 9 and 10 additionally require the optional
 ``physicsnemo`` extra (``pip install "physiotwin4d[physicsnemo]"``);
 PhysicsNeMo itself requires Python >= 3.11.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/quickstart.rst` around lines 276 - 279, Update the tutorial reference in
the quickstart documentation to use the consolidated tutorial numbering instead
of the removed 9a/9b/10a/10b suffixes, while preserving the existing physicsnemo
installation and Python version requirements.
docs/tutorials.rst (1)

89-90: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the outdated tutorial numbering.

The 1a-5a tutorial numbering scheme was removed in this PR. Please update this user-facing string to match the consolidated numbering.

📝 Proposed fix
-This covers the data used by Tutorials 1a-5a. Run
+This covers the data used by Tutorials 1-5. Run
 ``physiotwin4d-download-data --help`` for all options, and see
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tutorials.rst` around lines 89 - 90, Update the user-facing tutorial
reference in the surrounding documentation text to use the consolidated
numbering scheme instead of the removed “Tutorials 1a-5a” wording, while leaving
the download command and surrounding guidance unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/tutorials.rst`:
- Around line 449-450: Adjust the underline beneath “Tutorial 8: Fit the Cardiac
SSM and Propagate Through Gated Phases” to exactly match the title’s length,
preserving the existing section heading style.

In `@src/physiotwin4d/cli/reconstruct_highres_4d_ct.py`:
- Around line 97-105: Restore the --register-reference CLI default to disabled
so invocations that omit the flag preserve the previous identity-transform
behavior. Update the argparse definition for --register-reference, including its
help text, to accurately describe the disabled default and the explicit option
that enables registration; record the breaking default change in the release
notes or CHANGELOG.

In `@tests/test_tutorials.py`:
- Around line 281-282: Add `@pytest.mark.tutorial` and `@pytest.mark.slow`
decorators to the TestTutorial08BYODFitModel class, matching the marker pattern
used by the other tutorial test classes so it is selected by the tutorial and
slow test filters.

In `@tutorials/tutorial_01_lung_gated_ct_to_usd.py`:
- Around line 181-211: Update the screenshot asset lookup in the tutorial to use
the all-registration filename produced by WorkflowConvertImageToUSD, including
the “all” prefix in test_image_path so the registered slice screenshot executes.
Remove the nested test_labelmap_path screenshot block because this tutorial does
not pass dynamic_labelmap_ids and therefore does not produce per-frame labelmap
files.

In `@tutorials/tutorial_04_heart_create_statistical_model.py`:
- Around line 141-169: Pair the Xvfb startup in the tutorial’s rendering flow
with cleanup by wrapping the mode-rendering loop and screenshot generation in a
try/finally block, then call pv.stop_xvfb() in the finally clause. Preserve the
existing rendering behavior and ensure cleanup runs even when screenshot
creation or plotting raises an exception.

---

Outside diff comments:
In `@docs/quickstart.rst`:
- Around line 276-279: Update the tutorial reference in the quickstart
documentation to use the consolidated tutorial numbering instead of the removed
9a/9b/10a/10b suffixes, while preserving the existing physicsnemo installation
and Python version requirements.

In `@docs/tutorials.rst`:
- Around line 89-90: Update the user-facing tutorial reference in the
surrounding documentation text to use the consolidated numbering scheme instead
of the removed “Tutorials 1a-5a” wording, while leaving the download command and
surrounding guidance unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad0d4285-eebf-4321-b095-f88a20d11b39

📥 Commits

Reviewing files that changed from the base of the PR and between 02f1d90 and 065f974.

📒 Files selected for processing (23)
  • docs/architecture.rst
  • docs/index.rst
  • docs/quickstart.rst
  • docs/tutorials.rst
  • pyproject.toml
  • src/physiotwin4d/cli/reconstruct_highres_4d_ct.py
  • src/physiotwin4d/workflow_convert_image_to_usd.py
  • src/physiotwin4d/workflow_reconstruct_highres_4d_ct.py
  • tests/test_tutorials.py
  • tests/test_workflow_reconstruct_highres_4d_ct.py
  • tutorials/README.md
  • tutorials/tutorial_01_heart_gated_ct_to_usd.py
  • tutorials/tutorial_01_lung_gated_ct_to_usd.py
  • tutorials/tutorial_02_heart_ct_to_vtk.py
  • tutorials/tutorial_03_heart_vtk_to_usd.py
  • tutorials/tutorial_04_heart_create_statistical_model.py
  • tutorials/tutorial_05_heart_to_lung_fit_statistical_model_to_patient.py
  • tutorials/tutorial_06_lung_reconstruct_highres_4d_ct.py
  • tutorials/tutorial_08_byod_fit_model_to_patients.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mgn.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mlp.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mlp.py
🚧 Files skipped from review as they are similar to previous changes (8)
  • tutorials/tutorial_06_lung_reconstruct_highres_4d_ct.py
  • tutorials/tutorial_02_heart_ct_to_vtk.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mlp.py
  • tutorials/tutorial_10_byod_eval_physicsnemo_mgn.py
  • tutorials/tutorial_05_heart_to_lung_fit_statistical_model_to_patient.py
  • tutorials/tutorial_09_byod_train_physicsnemo_mgn.py
  • tutorials/tutorial_01_heart_gated_ct_to_usd.py
  • tutorials/tutorial_08_byod_fit_model_to_patients.py

Comment thread docs/tutorials.rst
Comment on lines +449 to +450
Tutorial 8: Fit the Cardiac SSM and Propagate Through Gated Phases
=================================================================

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the Sphinx title underline length.

The header underline is one character shorter than the title text (65 vs 66 characters). This will cause a Sphinx Title underline too short warning or build failure.

🐛 Proposed fix
 Tutorial 8: Fit the Cardiac SSM and Propagate Through Gated Phases
-=================================================================
+==================================================================
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Tutorial 8: Fit the Cardiac SSM and Propagate Through Gated Phases
=================================================================
Tutorial 8: Fit the Cardiac SSM and Propagate Through Gated Phases
==================================================================
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tutorials.rst` around lines 449 - 450, Adjust the underline beneath
“Tutorial 8: Fit the Cardiac SSM and Propagate Through Gated Phases” to exactly
match the title’s length, preserving the existing section heading style.

Comment on lines 97 to 105
parser.add_argument(
"--register-reference",
action="store_true",
default=False,
help="Register reference frame to fixed image (default: use identity)",
action=argparse.BooleanOptionalAction,
default=True,
help=(
"Register the reference time frame to the reference image "
"(default: enabled; use --no-register-reference for an identity transform)"
),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

--register-reference default silently flips from disabled to enabled.

This switches --register-reference from store_true (default False) to BooleanOptionalAction with default=True. Existing invocations that omit this flag will now get different reconstruction behavior (reference time frame registered to the reference image, instead of an identity transform) without any change to their command line. This aligns the CLI with WorkflowReconstructHighres4DCT's own default, but is a breaking behavior change for anyone relying on the old implicit default — worth a callout in release notes/CHANGELOG.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/physiotwin4d/cli/reconstruct_highres_4d_ct.py` around lines 97 - 105,
Restore the --register-reference CLI default to disabled so invocations that
omit the flag preserve the previous identity-transform behavior. Update the
argparse definition for --register-reference, including its help text, to
accurately describe the disabled default and the explicit option that enables
registration; record the breaking default change in the release notes or
CHANGELOG.

Comment thread tests/test_tutorials.py
Comment on lines +281 to +282
class TestTutorial08BYODFitModel:
"""End-to-end test for tutorial_08_byod_fit_model_to_patients.py."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add missing pytest markers to the BYOD test class.

TestTutorial08BYODFitModel is missing the @pytest.mark.tutorial and @pytest.mark.slow markers used by the other tutorial tests in this file. Without these markers, this test will not be executed when the tutorial test suite is run (e.g., via pytest -m tutorial).

💻 Proposed fix
+@pytest.mark.tutorial
+@pytest.mark.slow
 class TestTutorial08BYODFitModel:
     """End-to-end test for tutorial_08_byod_fit_model_to_patients.py."""
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class TestTutorial08BYODFitModel:
"""End-to-end test for tutorial_08_byod_fit_model_to_patients.py."""
`@pytest.mark.tutorial`
`@pytest.mark.slow`
class TestTutorial08BYODFitModel:
"""End-to-end test for tutorial_08_byod_fit_model_to_patients.py."""
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_tutorials.py` around lines 281 - 282, Add `@pytest.mark.tutorial`
and `@pytest.mark.slow` decorators to the TestTutorial08BYODFitModel class,
matching the marker pattern used by the other tutorial test classes so it is
selected by the tutorial and slow test filters.

Comment on lines +181 to 211
test_image_num = int(0.7 * len(input_filenames))
test_image_path = output_dir / f"slice_{test_image_num:03d}_registered.mha"
if test_image_path.exists():
test_image = itk.imread(str(test_image_path))
screenshots.append(
tt.save_screenshot_image_slice(
test_image,
f"slice_{test_image_num:03d}_registered_test.png",
axis=0,
slice_fraction=0.5,
colormap="gray",
vmin=-200,
vmax=600,
)
)

test_labelmap_path = output_dir / f"slice_{test_image_num:03d}_labelmap.mha"
if test_labelmap_path.exists():
test_labelmap = itk.imread(str(test_labelmap_path))
screenshots.append(
tt.save_screenshot_image_slice(
test_image,
f"slice_{test_image_num:03d}_registered_test.png",
f"slice_{test_image_num:03d}_labelmap_test.png",
axis=0,
slice_fraction=0.5,
colormap="gray",
vmin=-200,
vmax=600,
overlay_mask=test_labelmap,
)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Screenshot check uses the wrong asset filename; will never trigger.

Since no dynamic_labelmap_ids is passed to WorkflowConvertImageToUSD here, the workflow runs the "all" registration path, and workflow_convert_image_to_usd.py's _register() writes slice_{i:03d}_all_registered.mha (via filename_prefix=f"slice_{i:03d}_all"), not slice_{i:03d}_registered.mha. test_image_path.exists() will therefore always be False, silently skipping the intended slice/labelmap screenshots.

Additionally, the nested labelmap check (line 197) is dead even if the filename above is fixed: per-frame slice_{i:03d}_labelmap.mha is only written inside the dynamic_labelmap_ids branch of _segment_and_register_frames (see workflow_convert_image_to_usd.py lines 271-284), which this tutorial never exercises.

🐛 Proposed fix for the registered-slice filename
-test_image_path = output_dir / f"slice_{test_image_num:03d}_registered.mha"
+test_image_path = output_dir / f"slice_{test_image_num:03d}_all_registered.mha"

The nested test_labelmap_path block (lines 197-211) can be dropped for this tutorial (no dynamic/static split means no per-frame labelmap is ever exported), or kept only if you plan to pass dynamic_labelmap_ids in the future.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test_image_num = int(0.7 * len(input_filenames))
test_image_path = output_dir / f"slice_{test_image_num:03d}_registered.mha"
if test_image_path.exists():
test_image = itk.imread(str(test_image_path))
screenshots.append(
tt.save_screenshot_image_slice(
test_image,
f"slice_{test_image_num:03d}_registered_test.png",
axis=0,
slice_fraction=0.5,
colormap="gray",
vmin=-200,
vmax=600,
)
)
test_labelmap_path = output_dir / f"slice_{test_image_num:03d}_labelmap.mha"
if test_labelmap_path.exists():
test_labelmap = itk.imread(str(test_labelmap_path))
screenshots.append(
tt.save_screenshot_image_slice(
test_image,
f"slice_{test_image_num:03d}_registered_test.png",
f"slice_{test_image_num:03d}_labelmap_test.png",
axis=0,
slice_fraction=0.5,
colormap="gray",
vmin=-200,
vmax=600,
overlay_mask=test_labelmap,
)
)
test_image_num = int(0.7 * len(input_filenames))
test_image_path = output_dir / f"slice_{test_image_num:03d}_all_registered.mha"
if test_image_path.exists():
test_image = itk.imread(str(test_image_path))
screenshots.append(
tt.save_screenshot_image_slice(
test_image,
f"slice_{test_image_num:03d}_registered_test.png",
axis=0,
slice_fraction=0.5,
colormap="gray",
vmin=-200,
vmax=600,
)
)
test_labelmap_path = output_dir / f"slice_{test_image_num:03d}_labelmap.mha"
if test_labelmap_path.exists():
test_labelmap = itk.imread(str(test_labelmap_path))
screenshots.append(
tt.save_screenshot_image_slice(
test_image,
f"slice_{test_image_num:03d}_labelmap_test.png",
axis=0,
slice_fraction=0.5,
colormap="gray",
vmin=-200,
vmax=600,
overlay_mask=test_labelmap,
)
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/tutorial_01_lung_gated_ct_to_usd.py` around lines 181 - 211, Update
the screenshot asset lookup in the tutorial to use the all-registration filename
produced by WorkflowConvertImageToUSD, including the “all” prefix in
test_image_path so the registered slice screenshot executes. Remove the nested
test_labelmap_path screenshot block because this tutorial does not pass
dynamic_labelmap_ids and therefore does not produce per-frame labelmap files.

Comment on lines +141 to +169
try:
pv.start_xvfb()
except Exception:
pass

for mode_idx in range(mode_count):
sigma = float(np.sqrt(eigenvalues[mode_idx]))
mode_offsets = np.asarray(components[mode_idx]).reshape(-1, 3)

minus_mesh = mean_surface.copy()
minus_mesh.points = mean_points - 2.0 * sigma * mode_offsets
plus_mesh = mean_surface.copy()
plus_mesh.points = mean_points + 2.0 * sigma * mode_offsets

plotter = pv.Plotter(off_screen=True, window_size=[1200, 500], shape=(1, 3))
plotter.subplot(0, 0)
plotter.add_mesh(minus_mesh, color="royalblue", opacity=0.9)
plotter.camera_position = "iso"
plotter.subplot(0, 1)
plotter.add_mesh(mean_surface, color="steelblue", opacity=0.9)
plotter.camera_position = "iso"
plotter.subplot(0, 2)
plotter.add_mesh(plus_mesh, color="coral", opacity=0.9)
plotter.camera_position = "iso"

png_path = output_dir / f"pca_mode_{mode_idx + 1:02d}.png"
plotter.screenshot(str(png_path))
plotter.close()
screenshots.append(png_path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Xvfb display started but never stopped — leaks a display process.

pv.start_xvfb() is called at line 142 with no matching pv.stop_xvfb(). Existing helpers in test_tools.py (save_screenshot_mesh, save_screenshot_openusd) always pair start_xvfb/stop_xvfb in a try/finally. Left unpaired here, this can leave an orphaned Xvfb process running, especially problematic when multiple tutorials run sequentially in one CI session.

🔧 Proposed fix to stop Xvfb after rendering
-try:
-    pv.start_xvfb()
-except Exception:
-    pass
-
-for mode_idx in range(mode_count):
-    ...
-    screenshots.append(png_path)
+xvfb_started = False
+try:
+    pv.start_xvfb()
+    xvfb_started = True
+except Exception:
+    pass
+
+try:
+    for mode_idx in range(mode_count):
+        ...
+        screenshots.append(png_path)
+finally:
+    if xvfb_started and hasattr(pv, "stop_xvfb"):
+        pv.stop_xvfb()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
pv.start_xvfb()
except Exception:
pass
for mode_idx in range(mode_count):
sigma = float(np.sqrt(eigenvalues[mode_idx]))
mode_offsets = np.asarray(components[mode_idx]).reshape(-1, 3)
minus_mesh = mean_surface.copy()
minus_mesh.points = mean_points - 2.0 * sigma * mode_offsets
plus_mesh = mean_surface.copy()
plus_mesh.points = mean_points + 2.0 * sigma * mode_offsets
plotter = pv.Plotter(off_screen=True, window_size=[1200, 500], shape=(1, 3))
plotter.subplot(0, 0)
plotter.add_mesh(minus_mesh, color="royalblue", opacity=0.9)
plotter.camera_position = "iso"
plotter.subplot(0, 1)
plotter.add_mesh(mean_surface, color="steelblue", opacity=0.9)
plotter.camera_position = "iso"
plotter.subplot(0, 2)
plotter.add_mesh(plus_mesh, color="coral", opacity=0.9)
plotter.camera_position = "iso"
png_path = output_dir / f"pca_mode_{mode_idx + 1:02d}.png"
plotter.screenshot(str(png_path))
plotter.close()
screenshots.append(png_path)
xvfb_started = False
try:
pv.start_xvfb()
xvfb_started = True
except Exception:
pass
try:
for mode_idx in range(mode_count):
sigma = float(np.sqrt(eigenvalues[mode_idx]))
mode_offsets = np.asarray(components[mode_idx]).reshape(-1, 3)
minus_mesh = mean_surface.copy()
minus_mesh.points = mean_points - 2.0 * sigma * mode_offsets
plus_mesh = mean_surface.copy()
plus_mesh.points = mean_points + 2.0 * sigma * mode_offsets
plotter = pv.Plotter(off_screen=True, window_size=[1200, 500], shape=(1, 3))
plotter.subplot(0, 0)
plotter.add_mesh(minus_mesh, color="royalblue", opacity=0.9)
plotter.camera_position = "iso"
plotter.subplot(0, 1)
plotter.add_mesh(mean_surface, color="steelblue", opacity=0.9)
plotter.camera_position = "iso"
plotter.subplot(0, 2)
plotter.add_mesh(plus_mesh, color="coral", opacity=0.9)
plotter.camera_position = "iso"
png_path = output_dir / f"pca_mode_{mode_idx + 1:02d}.png"
plotter.screenshot(str(png_path))
plotter.close()
screenshots.append(png_path)
finally:
if xvfb_started and hasattr(pv, "stop_xvfb"):
pv.stop_xvfb()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/tutorial_04_heart_create_statistical_model.py` around lines 141 -
169, Pair the Xvfb startup in the tutorial’s rendering flow with cleanup by
wrapping the mode-rendering loop and screenshot generation in a try/finally
block, then call pv.stop_xvfb() in the finally clause. Preserve the existing
rendering behavior and ensure cleanup runs even when screenshot creation or
plotting raises an exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants