The Per-Subject Manifest

The manifest is the contract between your data and the training stack. It is the only thing you must produce to train on your own subjects: one JSON file per subject, naming a fitted reference mesh, that subject’s PCA shape parameters, the point-data array holding the training targets, and one entry per phase.

{
  "subject_id": "Case1Pack",
  "fitted_reference_mesh": "Case1Pack_ssm_surface.vtp",
  "pca_coefficients": "Case1Pack_ssm_pca_coefficients.json",
  "target_array": "displacement",
  "phases": [
    {"mesh": "Case1Pack_T00_ssm_surface_target.vtp", "stage": 0.0},
    {"mesh": "Case1Pack_T50_ssm_surface_target.vtp", "stage": 0.5}
  ]
}

Relative paths resolve against the manifest’s own directory.

Targets are read verbatim

The stack never derives targets from geometry. Whatever array target_array names is what the network learns to predict, and its width sets the network’s output size - three columns for a displacement, one for a scalar field, any number for something else. Tutorial 9 writes phase.points - reference.points into that array, which is what makes its model a motion model; write something else and the same code trains on it.

Every phase mesh must share the template’s point count and ordering, and stage is the caller’s own normalization of where the phase sits in the cycle - the workflow never parses filenames.

Meshes may be surfaces (.vtp) or volumes (.vtu); the template mesh decides which domain the model lives on.

Reference

ProcessPhysicsNemo is re-exported from the top-level package; SubjectManifest and PhaseEntry are not - import those by module:

from monai_physio import ProcessPhysicsNemo
from monai_physio.process_physicsnemo import SubjectManifest

manifest = ProcessPhysicsNemo.parse_manifest(manifest_path)
class monai_physio.process_physicsnemo.SubjectManifest(subject_id, fitted_reference_mesh, pca_coefficients, target_array, phases)[source]

A single subject’s training/inference inputs.

subject_id

Identifier used for output naming.

fitted_reference_mesh

The subject’s SSM surface as fitted to that subject by monai_physio.WorkflowFitStatisticalModelToPatient (.vtp surface or .vtu volume) — shape parameters and a deformable registration, never shape parameters alone. It supplies the point positions the targets are defined at; the stack never derives targets from it.

pca_coefficients

JSON file holding the subject’s PCA shape-parameter vector (a flat list of floats).

target_array

Name of the point-data array holding the target values in every phase mesh.

phases

One PhaseEntry per phase (at least one).

__init__(subject_id, fitted_reference_mesh, pca_coefficients, target_array, phases)
class monai_physio.process_physicsnemo.PhaseEntry(mesh, stage)[source]

One phase mesh carrying the target array, and its normalized stage.

__init__(mesh, stage)

Supporting helpers

class monai_physio.process_physicsnemo.ProcessPhysicsNemo[source]

Namespace of stateless helpers shared by the PhysicsNeMo workflows.

Every member is a @staticmethod: there is no instance state, so this is a grouping rather than an object to construct. Wrapping does not change when a method’s own local import torch/import torch_geometric runs - only at call time - so import monai_physio stays as lightweight as it was when these were bare functions.

static import_meshgraphnet()[source]

Import PhysicsNeMo’s MeshGraphNet, reporting install faults clearly.

PhysicsNeMo builds MeshGraphNet on torch_scatter, a compiled extension whose prebuilt wheels are published only for specific (torch, CUDA, Python, platform) combinations. A wheel built against a different torch than the installed one loads as an opaque OSError: Could not load this library: ..._scatter_cuda.pyd, which names neither torch_scatter nor the version mismatch that caused it. Translate that into a message that does.

Return type:

type[MeshGraphNet]

Returns:

The physicsnemo.models.meshgraphnet.MeshGraphNet class.

Raises:

ImportError – PhysicsNeMo or PyTorch Geometric could not be imported, or the installed torch_scatter binary does not match the installed torch.

static parse_manifest(manifest_path)[source]

Parse a per-subject JSON manifest.

Paths inside the manifest are resolved relative to the manifest’s own directory unless already absolute. Every phase must declare a stage.

Parameters:

manifest_path (Path) – Path to the subject manifest JSON file.

Return type:

SubjectManifest

Returns:

The parsed SubjectManifest.

Raises:
  • FileNotFoundError – If the manifest file does not exist.

  • ValueError – If required fields are missing, a phase lacks stage, or no phases are listed.

static load_pca_coefficients(path)[source]

Load a PCA shape-parameter vector saved as a JSON list of floats.

Return type:

ndarray

static load_target_array(path, array_name)[source]

Read one mesh’s target values out of its point data.

Parameters:
  • path (Path) – Mesh holding the targets (.vtp surface or .vtu volume).

  • array_name (str) – Point-data array name declared by the manifest.

Return type:

ndarray

Returns:

(n_points, n_target) float32 targets; a scalar array is returned as (n_points, 1).

Raises:

KeyError – If array_name is not among the mesh’s point-data arrays.

static build_node_features(mean_coords_norm, pca_norm, stage)[source]

Assemble per-vertex node features [coords_norm, pca_norm, stage].

Parameters:
  • mean_coords_norm (ndarray) – (n_points, 3) normalized mean-shape coordinates (identical for every subject/phase).

  • pca_norm (ndarray) – (n_pca,) normalized PCA shape parameters for the subject.

  • stage (float) – Normalized cardiac stage (RR-interval fraction) for the phase.

Return type:

ndarray

Returns:

(n_points, 3 + n_pca + 1) float32 feature array.

static mesh_to_edge_index(mesh)[source]

Build an undirected edge_index from a surface or volumetric mesh.

Parameters:

mesh (DataSet) – Template mesh whose cells encode the topology. pv.PolyData is read straight from its triangulated faces; any other dataset (a volumetric pv.UnstructuredGrid, for example) goes through extract_all_edges.

Return type:

Tensor

Returns:

(2, n_edges) long tensor of undirected edges indexing the mesh’s own points.

Raises:

ValueError – If edge extraction renumbers the points, which would break the correspondence between node features and graph nodes.

static compute_edge_features(coords, edge_index)[source]

Build (n_edges, 4) edge features [rel_x, rel_y, rel_z, distance].

Return type:

Tensor

static distributed_context()[source]

Initialize PhysicsNeMo’s DistributedManager and read it back.

DistributedManager.initialize picks up torchrun, SLURM or OpenMPI environments and falls back to a single process when it finds none, so this is safe to call from any entry point. Initialization is done once per process; calling this again returns the same context.

Raises:

ImportError – If the process was launched as one of several and PhysicsNeMo is not installed. Raised before anything is imported or written, so the caller has not yet created an output directory.

Return type:

DistributedContext

static unwrap_model(model)[source]

Return the bare module inside any torch.compile / DDP wrappers.

Both wrappers can be applied, in either order, so peel until neither attribute is left rather than checking for one of them.

Return type:

Any

static uncompiled_state_dict(model)[source]

Return a model’s state dict, unwrapping torch.compile and DDP.

Return type:

dict[str, Any]

static strip_compile_prefix(state)[source]

Strip the _orig_mod. prefix that torch.compile adds to keys.

Return type:

dict

class monai_physio.process_physicsnemo.PhaseSampleDataset(samples, mean_coords_norm, target_array, target_scale, cache_max_samples=0)[source]

Lazy provider of (node_features, normalized_target) samples.

One item is one (subject, phase) pair. Node features are rebuilt on access from the shared normalized template coordinates plus the subject’s normalized PCA parameters and the phase stage (cheap). Only the phase target arrays are read from disk, and those are held in a bounded LRU cache so an arbitrarily large training set streams from disk while a small set stays resident. Targets are returned as stored - the dataset never derives them from geometry.

Parameters:
  • samples (list[_Sample]) – Flat list of _Sample (built by the workflow).

  • mean_coords_norm (ndarray) – (n_points, 3) normalized template coordinates.

  • target_array (str) – Point-data array name holding the targets.

  • target_scale (float) – Target normalization factor (targets are divided by it so they land in ~[-1, 1]).

  • cache_max_samples (int) – Maximum decoded target arrays to cache. 0 means unbounded (all-in-RAM, fastest); a small value forces disk streaming.

__init__(samples, mean_coords_norm, target_array, target_scale, cache_max_samples=0)[source]
property n_points: int

Vertices per sample (shared across all subjects).

property n_features: int

Node feature dimension 3 + n_pca + 1.

property n_target: int

Target width (columns of the stored target array).

property subject_ids: list[str]

Subject id of every sample, in dataset order.

A physics residual is measured against the subject’s own reference geometry rather than the shared template, so a training method that adds one needs to know which subject a batch row came from.

__getitem__(index)[source]

Return (node_features, normalized_target) for one sample.

Return type:

tuple[ndarray, ndarray]

See Also