diff --git a/osipy/dce/t1_mapping/vfa.py b/osipy/dce/t1_mapping/vfa.py index 3eb1e75..c1aacb6 100644 --- a/osipy/dce/t1_mapping/vfa.py +++ b/osipy/dce/t1_mapping/vfa.py @@ -149,16 +149,23 @@ def _compute_t1_vfa_impl( raise DataValidationError(msg) params = dataset.acquisition_params - if not params.flip_angles: + + if params.flip_angles is None: msg = "VFA T1 mapping requires flip_angles in acquisition_params" raise DataValidationError(msg) + flip_angles = np.atleast_1d(np.asarray(params.flip_angles)) + + if flip_angles.size == 0: + msg = "VFA T1 mapping requires at least one flip angle" + raise DataValidationError(msg) + if params.tr is None: msg = "VFA T1 mapping requires TR in acquisition_params" raise DataValidationError(msg) xp = get_array_module(dataset.data) - flip_angles = xp.asarray(params.flip_angles) + flip_angles = xp.asarray(flip_angles) tr = params.tr # Check data dimensions match flip angles diff --git a/tests/unit/dce/test_t1_mapping.py b/tests/unit/dce/test_t1_mapping.py index 0f9bce6..c257601 100644 --- a/tests/unit/dce/test_t1_mapping.py +++ b/tests/unit/dce/test_t1_mapping.py @@ -13,6 +13,7 @@ from osipy.common.types import DCEAcquisitionParams, Modality from osipy.dce.t1_mapping.binding import BoundLookLockerModel, BoundSPGRModel from osipy.dce.t1_mapping.models import LookLockerSignalModel, SPGRSignalModel +from osipy.dce.t1_mapping.vfa import compute_t1_vfa # --------------------------------------------------------------------------- # Helpers for synthetic data generation @@ -407,7 +408,6 @@ class TestVFAFitting: def test_vfa_linear_recovers_t1(self) -> None: """VFA linear fit recovers known T1 from synthetic data.""" - from osipy.dce.t1_mapping.vfa import compute_t1_vfa t1_true = 1000.0 m0_true = 100.0 @@ -429,7 +429,6 @@ def test_vfa_linear_recovers_t1(self) -> None: def test_vfa_nonlinear_recovers_t1(self) -> None: """VFA nonlinear fit recovers known T1 from synthetic data.""" - from osipy.dce.t1_mapping.vfa import compute_t1_vfa t1_true = 1000.0 m0_true = 100.0 @@ -450,7 +449,6 @@ def test_vfa_nonlinear_recovers_t1(self) -> None: def test_vfa_array_interface(self) -> None: """VFA fitting works with individual arrays (no dataset).""" - from osipy.dce.t1_mapping.vfa import compute_t1_vfa t1_true = 800.0 m0_true = 150.0 @@ -469,9 +467,33 @@ def test_vfa_array_interface(self) -> None: t1_mean = np.nanmean(result.t1_map.values[result.quality_mask]) np.testing.assert_allclose(t1_mean, t1_true, rtol=0.01) + def test_vfa_accepts_numpy_flip_angles(self) -> None: + """VFA fitting accepts NumPy arrays for flip_angles in the dataset.""" + + t1_true = 1000.0 + m0_true = 100.0 + tr = 5.0 + + flip_angles = np.array([2.0, 5.0, 10.0, 15.0, 20.0]) + + dataset = _make_vfa_dataset( + t1=t1_true, + m0=m0_true, + flip_angles=flip_angles.tolist(), + tr=tr, + ) + + # Replace the list with a NumPy array to reproduce the reported bug + dataset.acquisition_params.flip_angles = flip_angles + + result = compute_t1_vfa(dataset, method="linear") + + t1_mean = np.nanmean(result.t1_map.values[result.quality_mask]) + + np.testing.assert_allclose(t1_mean, t1_true, rtol=0.01) + def test_vfa_invalid_method_raises(self) -> None: """Unknown VFA method raises DataValidationError.""" - from osipy.dce.t1_mapping.vfa import compute_t1_vfa dataset = _make_vfa_dataset(1000.0, 100.0, [2.0, 5.0, 10.0], 5.0) with pytest.raises(DataValidationError, match="Unknown VFA method"):