@@ -498,3 +498,70 @@ def test_the_epoch_log_separates_the_two_loss_terms() -> None:
498498 assert "physics=0.000000" in messages [- 1 ], (
499499 f"An epoch that accumulated nothing should report zero: { messages [- 1 ]} "
500500 )
501+
502+
503+ def test_bind_reference_meshes_repairs_against_template_elements (tmp_path : Any ) -> None :
504+ """Repair must use ``self._tets``, not whatever cells the file stores.
505+
506+ A fitted reference file's own connectivity is never read back by
507+ ``tet_volumes`` -- only ``self._tets`` is -- so repairing against the
508+ file's cells instead would validate the wrong topology. Here the file
509+ stores one degenerate, unrecoverable cell that touches only a single
510+ node; repairing against it would raise, but ``self._tets`` names a
511+ perfectly valid mesh, so binding must succeed unchanged.
512+ """
513+ import torch
514+
515+ from monai_physio .physicsnemo_tools import DistributedContext
516+ from monai_physio .train_physicsnemo_physics_informed_motion import (
517+ TrainPhysicsNeMoPhysicsInformedMotion ,
518+ )
519+
520+ points , tets = _grid_mesh (size = 3 )
521+ mismatched = pv .UnstructuredGrid (
522+ {pv .CellType .TETRA : np .array ([[0 , 0 , 0 , 0 ]])}, points
523+ )
524+ mesh_path = tmp_path / "reference.vtu"
525+ mismatched .save (mesh_path )
526+
527+ method = TrainPhysicsNeMoPhysicsInformedMotion ()
528+ method ._tets = tets
529+ method ._sample_subjects = ["subj0" ]
530+ method ._reference_meshes = {"subj0" : mesh_path }
531+ context = DistributedContext (
532+ device = torch .device ("cpu" ), rank = 0 , local_rank = 0 , world_size = 1
533+ )
534+
535+ method ._bind_reference_meshes (context , n_points = len (points ))
536+
537+ reference , volumes = method ._reference_cache ["subj0" ]
538+ assert np .allclose (reference .numpy (), points , atol = 1e-5 )
539+ assert torch .all (volumes > 0 )
540+
541+
542+ def test_bind_reference_meshes_tolerates_a_file_with_no_cells (tmp_path : Any ) -> None :
543+ """A reference file need not carry any cells at all; only its points do."""
544+ import torch
545+
546+ from monai_physio .physicsnemo_tools import DistributedContext
547+ from monai_physio .train_physicsnemo_physics_informed_motion import (
548+ TrainPhysicsNeMoPhysicsInformedMotion ,
549+ )
550+
551+ points , tets = _grid_mesh (size = 3 )
552+ mesh_path = tmp_path / "reference.vtp"
553+ pv .PolyData (points ).save (mesh_path )
554+
555+ method = TrainPhysicsNeMoPhysicsInformedMotion ()
556+ method ._tets = tets
557+ method ._sample_subjects = ["subj0" ]
558+ method ._reference_meshes = {"subj0" : mesh_path }
559+ context = DistributedContext (
560+ device = torch .device ("cpu" ), rank = 0 , local_rank = 0 , world_size = 1
561+ )
562+
563+ method ._bind_reference_meshes (context , n_points = len (points ))
564+
565+ reference , volumes = method ._reference_cache ["subj0" ]
566+ assert np .allclose (reference .numpy (), points , atol = 1e-5 )
567+ assert torch .all (volumes > 0 )
0 commit comments