From fd8db46adce505e456c61ae561d091c90637fcb3 Mon Sep 17 00:00:00 2001 From: Parikshit Date: Sat, 11 Jul 2026 03:16:24 +0530 Subject: [PATCH 1/2] Fix VFA validation for NumPy flip angles --- osipy/dce/t1_mapping/vfa.py | 13 +++++++++++-- tests/unit/dce/test_t1_mapping.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/osipy/dce/t1_mapping/vfa.py b/osipy/dce/t1_mapping/vfa.py index 3eb1e75..0a2f6b0 100644 --- a/osipy/dce/t1_mapping/vfa.py +++ b/osipy/dce/t1_mapping/vfa.py @@ -24,6 +24,8 @@ import numpy as np +from osipy.common import dataset +from osipy.common import dataset from osipy.common.backend.array_module import get_array_module, to_numpy from osipy.common.dataset import PerfusionDataset from osipy.common.exceptions import DataValidationError @@ -149,16 +151,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..edfba6f 100644 --- a/tests/unit/dce/test_t1_mapping.py +++ b/tests/unit/dce/test_t1_mapping.py @@ -4,7 +4,11 @@ signal models, binding adapters, and Jacobian accuracy. """ +from unittest import result + import numpy as np +from osipy.common import dataset +from osipy.dce.t1_mapping.vfa import compute_t1_vfa import pytest from osipy.common.dataset import PerfusionDataset @@ -468,6 +472,32 @@ 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.""" + from osipy.dce.t1_mapping.vfa import compute_t1_vfa + + 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 1031614045c365c2399e7a3b03ad48a22794096c Mon Sep 17 00:00:00 2001 From: Parikshit Date: Tue, 14 Jul 2026 17:43:59 +0530 Subject: [PATCH 2/2] Apply pre-commit formatting --- osipy/dce/t1_mapping/vfa.py | 2 -- tests/unit/dce/test_t1_mapping.py | 14 +++----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/osipy/dce/t1_mapping/vfa.py b/osipy/dce/t1_mapping/vfa.py index 0a2f6b0..c1aacb6 100644 --- a/osipy/dce/t1_mapping/vfa.py +++ b/osipy/dce/t1_mapping/vfa.py @@ -24,8 +24,6 @@ import numpy as np -from osipy.common import dataset -from osipy.common import dataset from osipy.common.backend.array_module import get_array_module, to_numpy from osipy.common.dataset import PerfusionDataset from osipy.common.exceptions import DataValidationError diff --git a/tests/unit/dce/test_t1_mapping.py b/tests/unit/dce/test_t1_mapping.py index edfba6f..c257601 100644 --- a/tests/unit/dce/test_t1_mapping.py +++ b/tests/unit/dce/test_t1_mapping.py @@ -4,11 +4,7 @@ signal models, binding adapters, and Jacobian accuracy. """ -from unittest import result - import numpy as np -from osipy.common import dataset -from osipy.dce.t1_mapping.vfa import compute_t1_vfa import pytest from osipy.common.dataset import PerfusionDataset @@ -17,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 @@ -411,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 @@ -433,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 @@ -454,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 @@ -472,10 +466,9 @@ 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.""" - from osipy.dce.t1_mapping.vfa import compute_t1_vfa t1_true = 1000.0 m0_true = 100.0 @@ -497,11 +490,10 @@ def test_vfa_accepts_numpy_flip_angles(self) -> None: t1_mean = np.nanmean(result.t1_map.values[result.quality_mask]) - np.testing.assert_allclose(t1_mean, t1_true, rtol=0.01) + 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"):