From 672bdf4357f815c63dfab91d9c8e257266bceb21 Mon Sep 17 00:00:00 2001 From: "Lumberbot (aka Jack)" <39504233+meeseeksmachine@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:15:42 -0800 Subject: [PATCH] Backport PR #13062 on branch maint/1.9 (BUG: Fix bug with interval calculation) (#13064) Co-authored-by: Eric Larson --- .pre-commit-config.yaml | 2 +- README.rst | 2 +- azure-pipelines.yml | 2 +- doc/changes/devel/13062.bugfix.rst | 1 + mne/preprocessing/_fine_cal.py | 15 +++++++++------ mne/preprocessing/tests/test_fine_cal.py | 14 +++++++++++++- 6 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 doc/changes/devel/13062.bugfix.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e7a931b5713..06918b99de1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -78,7 +78,7 @@ repos: language: python entry: ./tools/hooks/sync_dependencies.py files: pyproject.toml - additional_dependencies: ["mne"] + additional_dependencies: ["mne==1.9.0"] # zizmor - repo: https://github.com/woodruffw/zizmor-pre-commit diff --git a/README.rst b/README.rst index 50e0daaa52c..cfa7488324f 100644 --- a/README.rst +++ b/README.rst @@ -72,7 +72,7 @@ The minimum required dependencies to run MNE-Python are: .. ↓↓↓ BEGIN CORE DEPS LIST. DO NOT EDIT! HANDLED BY PRE-COMMIT HOOK ↓↓↓ -- `Python `__ ≥ 3.9 +- `Python `__ ≥ 3.10 - `NumPy `__ ≥ 1.23 - `SciPy `__ ≥ 1.9 - `Matplotlib `__ ≥ 3.6 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3ca4177174f..2bf2cd07bd3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -244,7 +244,7 @@ stages: PYTHONIOENCODING: 'utf-8' AZURE_CI_WINDOWS: 'true' PYTHON_ARCH: 'x64' - timeoutInMinutes: 75 + timeoutInMinutes: 80 strategy: maxParallel: 4 matrix: diff --git a/doc/changes/devel/13062.bugfix.rst b/doc/changes/devel/13062.bugfix.rst new file mode 100644 index 00000000000..9e01fc4c835 --- /dev/null +++ b/doc/changes/devel/13062.bugfix.rst @@ -0,0 +1 @@ +Fix computation of time intervals in :func:`mne.preprocessing.compute_fine_calibration` by `Eric Larson`_. diff --git a/mne/preprocessing/_fine_cal.py b/mne/preprocessing/_fine_cal.py index 41d20539ce0..b43983a87eb 100644 --- a/mne/preprocessing/_fine_cal.py +++ b/mne/preprocessing/_fine_cal.py @@ -156,11 +156,12 @@ def compute_fine_calibration( # 1. Rotate surface normals using magnetometer information (if present) # cals = np.ones(len(info["ch_names"])) - time_idxs = raw.time_as_index(np.arange(0.0, raw.times[-1], t_window)) - if len(time_idxs) <= 1: - time_idxs = np.array([0, len(raw.times)], int) - else: - time_idxs[-1] = len(raw.times) + end = len(raw.times) + 1 + time_idxs = np.arange(0, end, int(round(t_window * raw.info["sfreq"]))) + if len(time_idxs) == 1: + time_idxs = np.concatenate([time_idxs, [end]]) + if time_idxs[-1] != end: + time_idxs[-1] = end count = 0 locs = np.array([ch["loc"] for ch in info["chs"]]) zs = locs[mag_picks, -3:].copy() @@ -388,9 +389,11 @@ def _adjust_mag_normals(info, data, origin, ext_order, *, angle_limit, err_limit each_err = _data_err(data, S_tot, cals, axis=-1)[picks_mag] n_bad = (each_err > err_limit).sum() if n_bad: + bad_max = np.argmax(each_err) reason.append( f"{n_bad} residual{_pl(n_bad)} > {err_limit:0.1f}% " - f"(max: {each_err.max():0.2f}%)" + f"(max: {each_err[bad_max]:0.2f}% @ " + f"{info['ch_names'][picks_mag[bad_max]]})" ) reason = ", ".join(reason) if reason: diff --git a/mne/preprocessing/tests/test_fine_cal.py b/mne/preprocessing/tests/test_fine_cal.py index 02c596bf4bc..45971620db5 100644 --- a/mne/preprocessing/tests/test_fine_cal.py +++ b/mne/preprocessing/tests/test_fine_cal.py @@ -20,7 +20,7 @@ ) from mne.preprocessing.tests.test_maxwell import _assert_shielding from mne.transforms import _angle_dist_between_rigid -from mne.utils import object_diff +from mne.utils import catch_logging, object_diff # Define fine calibration filepaths data_path = testing.data_path(download=False) @@ -289,3 +289,15 @@ def test_fine_cal_systems(system, tmp_path): got_corrs = np.corrcoef([raw_data, raw_sss_data, raw_sss_cal_data]) got_corrs = got_corrs[np.triu_indices(3, 1)] assert_allclose(got_corrs, corrs, atol=corr_tol) + if system == "fil": + with catch_logging(verbose=True) as log: + compute_fine_calibration( + raw.copy().crop(0, 0.12).pick(raw.ch_names[:12]), + t_window=0.06, # 2 segments + angle_limit=angle_limit, + err_limit=err_limit, + ext_order=2, + verbose=True, + ) + log = log.getvalue() + assert "(averaging over 2 time intervals)" in log, log