Skip to content

[FEAT] MEDIC distortion correction via warpkit - #541

Open
vanandrew wants to merge 14 commits into
nipreps:mainfrom
vanandrew:feat/medic-warpkit
Open

vanandrew wants to merge 14 commits into
nipreps:mainfrom
vanandrew:feat/medic-warpkit

Conversation

@vanandrew

@vanandrew vanandrew commented Apr 26, 2026 •

Copy link
Copy Markdown

Summary

Adds multi-echo dynamic distortion correction (MEDIC) to sdcflows, backed by warpkit. MEDIC estimates a per-volume B0 fieldmap directly from a multi-echo, mag+phase BOLD series, capturing breathing- and motion-driven field changes that a single static fieldmap can't.

Revives #435 / #438 with the simpler pure-Python warpkit (post vanandrew/warpkit#16, no Julia/C++ setup required). Closes #36.

What's new

Workflows

  • init_medic_wf (sdcflows/workflows/fit/medic.py) — multi-echo phase + magnitude → 4D Hz fieldmap (one volume per timepoint, on the EPI grid), with the first-echo magnitude series as fmap_ref and warpkit's per-frame masks as fmap_mask. Two-stage warpkit call (UnwrapPhase → ComputeFieldmap) so the per-frame masks stay accessible. Single fmap output is 4D for MEDIC, leaving the 3D-vs-4D dispatch to the apply consumer.
  • init_dynamic_unwarp_wf (sdcflows/workflows/apply/dynamic.py) — per-volume apply path built on sdcflows.transform.apply_dynamic_unwarp, a per-frame extension of the same scipy/nitransforms-backed resampling that powers the static init_unwarp_wf. No warpkit needed at runtime. Includes Jacobian determinant intensity correction (jacobian=True default) sharing transform.fieldmap_jacobian with the static path.

Interfaces

  • sdcflows/interfaces/warpkit.py — thin LibraryBaseInterface wrappers around the two MEDIC stages SDCFlows actually drives: UnwrapPhase (ROMEO) and ComputeFieldmap. Lazy import — sdcflows imports warpkit only when these interfaces actually run.

Detection / dispatch

  • EstimatorType.MEDIC added. FieldmapEstimation.__attrs_post_init__ detects MEDIC inputs (part-{phase,mag} on bold/epi/sbref sources), enforces ≥2 echoes with exactly one phase and one magnitude file per echo entity (matching EchoTime), and rejects partial or mixed part sets. get_workflow instantiates init_medic_wf with EchoTime-sorted, echo-paired lists (BIDS doesn't guarantee echo entity == numeric order).
  • Wrangler seeds MEDIC detection from both part='phase' and part='mag' queries (some datasets carry IntendedFor only on one side); dedup walks the full sibling set, no reliance on pybids ordering.

Plumbing

  • init_fmap_preproc_wf skips fmap_coeff for MEDIC (the fieldmap is on the EPI grid by construction, no B-spline rep).
  • init_fmap_derivatives_wf uses MergeSeries(allow_4D=True) so MEDIC's 4D fmap passes through the same ds_fieldmap sink as the 3D static fmaps.

Packaging

  • warpkit is a core dependency (per review discussion, rather than an extras group). Its non-commercial WUSTL license is documented in the init_medic_wf module docstring; the interfaces import warpkit lazily, only when they run.
  • CI: datalad-fetches sub-04/ses-2 of ds007637 and sub-a01 of ds006926 as MEDIC fixtures (cache key bumped to v3).

Validation

  • Three-layer multi-echo guard: wrangler seed query (echo=Query.REQUIRED), FieldmapEstimation cardinality check (len(phase_files) < 2), and _unpack_metadata runtime guard inside init_medic_wf.
  • Construction tests for both workflows run on every CI lane (no warpkit needed).
  • test_apply_dynamic_unwarp_matches_static pins apply_dynamic_unwarp to the same Hz→VSM + scipy.ndimage convention as the static _sdc_unwarp path — catches drift in sign / pe_info handling.
  • test_wrangler_filter / test_wrangler_URIs parametrized with a 3-session × 3-echo × {mag,phase} BIDS skeleton.
  • End-to-end runs on ds006926 (3-echo, 64×64×40, 205 frames) and ds007637 (5-echo, 110×110×72, 237 frames) confirm Jacobian factor sits at ~1.0 in the brain mask (std 0.087 / 0.118) with ~1–2% of voxels in compression/expansion regions and total signal change <2% — physically sensible.

Known compromises

  • MEDIC discovery keys off B0FieldIdentifier (or, legacy, IntendedFor) only — same constraint as the existing single-PE EPI branch. Datasets missing both need that metadata added; there is no structure-only opt-in (force_medic was dropped in review).
  • The single fmap output is 4D for MEDIC. Downstream tools that expect 3D field maps need to either dispatch on dimensionality or block MEDIC-based estimators until they do.

Test plan

  • pytest sdcflows/utils/tests/test_wrangler.py — wrangler MEDIC detection paths (including no_medic)
  • pytest sdcflows/workflows/fit/tests/test_medic.py — init_medic_wf construction + _unpack_metadata guards
  • pytest sdcflows/workflows/apply/tests/test_dynamic.py — init_dynamic_unwarp_wf construction + jacobian flag + per-frame resampling vs. static path
  • pytest -m veryslow with pip install sdcflows[warpkit] and ds006926 / ds007637 fixtures present — full MEDIC fit + dynamic apply

@codecov

codecov Bot commented Apr 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.18182% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.70%. Comparing base (b04e403) to head (99b7b49).

Files with missing lines Patch % Lines
sdcflows/workflows/apply/dynamic.py 75.00% 7 Missing ⚠️
sdcflows/fieldmaps.py 86.04% 2 Missing and 4 partials ⚠️
sdcflows/transform.py 87.75% 3 Missing and 3 partials ⚠️
sdcflows/utils/wrangler.py 86.66% 3 Missing and 1 partial ⚠️
sdcflows/workflows/fit/base.py 33.33% 1 Missing and 1 partial ⚠️
sdcflows/interfaces/reportlets.py 90.90% 0 Missing and 1 partial ⚠️
sdcflows/workflows/base.py 93.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #541      +/-   ##
==========================================
+ Coverage   84.13%   85.70%   +1.56%     
==========================================
  Files          30       33       +3     
  Lines        2938     3175     +237     
  Branches      401      458      +57     
==========================================
+ Hits         2472     2721     +249     
+ Misses        388      373      -15     
- Partials       78       81       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vanandrew
vanandrew force-pushed the feat/medic-warpkit branch from 57ccc47 to 21ab1c6 Compare May 10, 2026 03:21
@vanandrew
vanandrew marked this pull request as ready for review May 10, 2026 04:49
@vanandrew vanandrew changed the title [WIP] [FEAT] MEDIC distortion correction via warpkit [FEAT] MEDIC distortion correction via warpkit May 11, 2026
@vanandrew
vanandrew force-pushed the feat/medic-warpkit branch 6 times, most recently from bcaf4ad to 9ecfbae Compare May 13, 2026 03:10
vanandrew added a commit to vanandrew/sdcflows that referenced this pull request May 14, 2026
Three additions targeting the codecov/patch failure on nipreps#541:

* sdcflows/interfaces/tests/test_warpkit.py (new) — instantiates every
  warpkit-backed interface so its spec class body is hit at import (was
  0% on codecov despite running locally; likely an xdist worker-merge
  artefact). Also covers ``_as_str_list``, the ``_pkg`` invariant, and
  the ``border_filt=(1, 5)`` traits default regression.

* sdcflows/workflows/tests/test_outputs.py (new) — direct construction
  test for ``init_fmap_derivatives_wf``, exercising both the default
  and ``write_dynamic=True`` paths. The MEDIC dynamic sinks branch was
  only reached transitively from the ``test_fmap_wf`` slow test, hence
  0% patch coverage on outputs.py.

* sdcflows/workflows/fit/tests/test_medic.py — fill the remaining
  helper gaps: empty-metadata rejection, ``sloppy=True`` zooms_min,
  ``_first``, and ``_temporal_mean`` for both 3D and 4D inputs.

The ``_run_interface`` method bodies in interfaces/warpkit.py remain
uncovered in the fast/slow envs since they require warpkit to import;
the existing ``veryslow`` MEDIC fixtures exercise them when the optional
dependency is installed.
@vanandrew

Copy link
Copy Markdown
Author

I have no idea why test (3.13, pre, fast) is failing on the cache step.

It looks like an out-of-space error on the runner (but the other jobs with the same steps succeeded, so very confused):

[test (3.13, pre, fast)](https://github.com/nipreps/sdcflows/actions/runs/25840682073/job/75990934202)
System.IO.IOException: No space left on device : '/home/runner/actions-runner/cached/2.334.0/_diag/Worker_20260514-125354-utc.log'
   at System.IO.RandomAccess.WriteAtOffset(SafeFileHandle handle, ReadOnlySpan`1 buffer, Int64 fileOffset)
   at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
   at System.Diagnostics.TextWriterTraceListener.Flush()
   at GitHub.Runner.Common.HostTraceListener.WriteHeader(String source, TraceEventType eventType, Int32 id)
   at System.Diagnostics.TraceSource.TraceEvent(TraceEventType eventType, Int32 id, String message)
   at GitHub.Runner.Worker.Worker.RunAsync(String pipeIn, String pipeOut)
   at GitHub.Runner.Worker.Program.MainAsync(IHostContext context, String[] args)
System.IO.IOException: No space left on device : '/home/runner/actions-runner/cached/2.334.0/_diag/Worker_20260514-125354-utc.log'
   at System.IO.RandomAccess.WriteAtOffset(SafeFileHandle handle, ReadOnlySpan`1 buffer, Int64 fileOffset)
   at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
   at System.Diagnostics.TextWriterTraceListener.Flush()
   at GitHub.Runner.Common.HostTraceListener.WriteHeader(String source, TraceEventType eventType, Int32 id)
   at System.Diagnostics.TraceSource.TraceEvent(TraceEventType eventType, Int32 id, String message)
   at GitHub.Runner.Common.Tracing.Error(Exception exception)
   at GitHub.Runner.Worker.Program.MainAsync(IHostContext context, String[] args)
Unhandled exception. System.IO.IOException: No space left on device : '/home/runner/actions-runner/cached/2.334.0/_diag/Worker_20260514-125354-utc.log'
   at System.IO.RandomAccess.WriteAtOffset(SafeFileHandle handle, ReadOnlySpan`1 buffer, Int64 fileOffset)
   at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
   at System.Diagnostics.TextWriterTraceListener.Flush()
   at System.Diagnostics.TraceSource.Flush()
   at GitHub.Runner.Common.Tracing.Dispose(Boolean disposing)
   at GitHub.Runner.Common.Tracing.Dispose()
   at GitHub.Runner.Common.TraceManager.Dispose(Boolean disposing)
   at GitHub.Runner.Common.TraceManager.Dispose()
   at GitHub.Runner.Common.HostContext.Dispose(Boolean disposing)
   at GitHub.Runner.Common.HostContext.Dispose()
   at GitHub.Runner.Worker.Program.Main(String[] args)

@tsalo

tsalo commented May 15, 2026

Copy link
Copy Markdown
Contributor

@vanandrew I think someone must have rerun that failing job and now it's passing.

@tsalo tsalo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! I made a quick pass through the code.

Comment thread sdcflows/interfaces/warpkit.py Outdated
Comment thread sdcflows/interfaces/warpkit.py Outdated
Comment thread sdcflows/interfaces/warpkit.py Outdated
Comment thread sdcflows/utils/wrangler.py Outdated
Comment thread sdcflows/workflows/apply/dynamic.py Outdated
Comment thread sdcflows/workflows/fit/medic.py Outdated
Comment thread sdcflows/workflows/fit/medic.py Outdated
Comment thread sdcflows/utils/wrangler.py
@vanandrew

Copy link
Copy Markdown
Author

Pushed four commits addressing your feedback (noise_frames, ctor inputs, nitransforms switch + interface cleanup, unified fmap output).

@vanandrew

Copy link
Copy Markdown
Author

I can't tell if these test failures are something wrong with my dataset additions or if it's just another one-off error:

update(error): . (dataset) [Fetch failed: CommandError(CommandError: 'git -c diff.ignoreSubmodules=none -c core.quotepath=false fetch --verbose --progress --no-recurse-submodules --
prune origin' failed with exitcode 128 under /home/runner/sdcflows-tests/brain-extraction-tests [err: 'fatal: unable to access 'https://gin.g-node.org/nipreps-data/brain-extraction-
tests/': The requested URL returned error: 403'])] [CommandError: 'git -c diff.ignoreSubmodules=none -c core.quotepath=false fetch --verbose --progress --no-recurse-submodules --
prune origin' failed with exitcode 128 under /home/runner/sdcflows-tests/brain-extraction-tests [err: 'fatal: unable to access 'https://gin.g-node.org/nipreps-data/brain-extraction-
tests/': The requested URL returned error: 403']]
Error: Process completed with exit code 1.

@vanandrew
vanandrew requested a review from tsalo May 22, 2026 04:27
@vanandrew
vanandrew force-pushed the feat/medic-warpkit branch from a4c4781 to b070878 Compare May 23, 2026 14:24
@vanandrew

Copy link
Copy Markdown
Author

Looks like the CI keeps failing because it's running out of space (I added two datasets for tests so that might have pushed it over the edge).

I pushed 6f5cfff2 adding a jlumbroso/free-disk-space@main step at the top of the test job:

- name: Free disk space
  uses: jlumbroso/free-disk-space@main
  with:
    tool-cache: false
    android: true
    dotnet: true
    haskell: true
    large-packages: false
    swap-storage: false

This gets rid of some unneeded development tools on the github runner to free room for data.

@vanandrew

Copy link
Copy Markdown
Author

@tsalo Alright. I made changes based on our convo today:

  • find_estimators discovers a fieldmap estimator for every fieldmap declared in BIDS intent metadata: grouping inputs by B0FieldIdentifier (Step 1), or falling back to IntendedFor heuristics (Step 2) only when no B0FieldIdentifier exists. (no_medic suppresses MEDIC via either route). It adds a sort that always returns dynamic estimators (i.e. MEDIC) first.
  • Simplified fmap_ref and fmap_mask to be the first echo magntude and MEDIC fmap masks respectively. These likely won't be used by downstream consumers.
  • Removed force_medic and replaced with no_medic

Comment thread sdcflows/workflows/fit/medic.py Outdated
Comment thread pyproject.toml Outdated
vanandrew added a commit to vanandrew/sdcflows that referenced this pull request Jun 2, 2026
Three additions targeting the codecov/patch failure on nipreps#541:

* sdcflows/interfaces/tests/test_warpkit.py (new) — instantiates every
  warpkit-backed interface so its spec class body is hit at import (was
  0% on codecov despite running locally; likely an xdist worker-merge
  artefact). Also covers ``_as_str_list``, the ``_pkg`` invariant, and
  the ``border_filt=(1, 5)`` traits default regression.

* sdcflows/workflows/tests/test_outputs.py (new) — direct construction
  test for ``init_fmap_derivatives_wf``, exercising both the default
  and ``write_dynamic=True`` paths. The MEDIC dynamic sinks branch was
  only reached transitively from the ``test_fmap_wf`` slow test, hence
  0% patch coverage on outputs.py.

* sdcflows/workflows/fit/tests/test_medic.py — fill the remaining
  helper gaps: empty-metadata rejection, ``sloppy=True`` zooms_min,
  ``_first``, and ``_temporal_mean`` for both 3D and 4D inputs.

The ``_run_interface`` method bodies in interfaces/warpkit.py remain
uncovered in the fast/slow envs since they require warpkit to import;
the existing ``veryslow`` MEDIC fixtures exercise them when the optional
dependency is installed.
@vanandrew
vanandrew force-pushed the feat/medic-warpkit branch from da9d07b to c55efd0 Compare June 2, 2026 20:36
vanandrew added a commit to vanandrew/sdcflows that referenced this pull request Jun 2, 2026
Three additions targeting the codecov/patch failure on nipreps#541:

* sdcflows/interfaces/tests/test_warpkit.py (new) — instantiates every
  warpkit-backed interface so its spec class body is hit at import (was
  0% on codecov despite running locally; likely an xdist worker-merge
  artefact). Also covers ``_as_str_list``, the ``_pkg`` invariant, and
  the ``border_filt=(1, 5)`` traits default regression.

* sdcflows/workflows/tests/test_outputs.py (new) — direct construction
  test for ``init_fmap_derivatives_wf``, exercising both the default
  and ``write_dynamic=True`` paths. The MEDIC dynamic sinks branch was
  only reached transitively from the ``test_fmap_wf`` slow test, hence
  0% patch coverage on outputs.py.

* sdcflows/workflows/fit/tests/test_medic.py — fill the remaining
  helper gaps: empty-metadata rejection, ``sloppy=True`` zooms_min,
  ``_first``, and ``_temporal_mean`` for both 3D and 4D inputs.

The ``_run_interface`` method bodies in interfaces/warpkit.py remain
uncovered in the fast/slow envs since they require warpkit to import;
the existing ``veryslow`` MEDIC fixtures exercise them when the optional
dependency is installed.
@vanandrew
vanandrew force-pushed the feat/medic-warpkit branch from c55efd0 to b0a6904 Compare June 2, 2026 20:37
@vanandrew

Copy link
Copy Markdown
Author

@tsalo I did a rebase to the latest main, hopefully to quash the 3.13 test error that happened in the last CI run: https://github.com/nipreps/sdcflows/actions/runs/26792754492/job/79155816998#step:22:320

@vanandrew
vanandrew requested a review from effigies July 23, 2026 03:51
vanandrew added a commit to vanandrew/sdcflows that referenced this pull request Aug 9, 2026
Three additions targeting the codecov/patch failure on nipreps#541:

* sdcflows/interfaces/tests/test_warpkit.py (new) — instantiates every
  warpkit-backed interface so its spec class body is hit at import (was
  0% on codecov despite running locally; likely an xdist worker-merge
  artefact). Also covers ``_as_str_list``, the ``_pkg`` invariant, and
  the ``border_filt=(1, 5)`` traits default regression.

* sdcflows/workflows/tests/test_outputs.py (new) — direct construction
  test for ``init_fmap_derivatives_wf``, exercising both the default
  and ``write_dynamic=True`` paths. The MEDIC dynamic sinks branch was
  only reached transitively from the ``test_fmap_wf`` slow test, hence
  0% patch coverage on outputs.py.

* sdcflows/workflows/fit/tests/test_medic.py — fill the remaining
  helper gaps: empty-metadata rejection, ``sloppy=True`` zooms_min,
  ``_first``, and ``_temporal_mean`` for both 3D and 4D inputs.

The ``_run_interface`` method bodies in interfaces/warpkit.py remain
uncovered in the fast/slow envs since they require warpkit to import;
the existing ``veryslow`` MEDIC fixtures exercise them when the optional
dependency is installed.
@vanandrew
vanandrew force-pushed the feat/medic-warpkit branch from 71f7ba7 to aab352e Compare August 9, 2026 01:18
@vanandrew

Copy link
Copy Markdown
Author

I did a reorg of the git history for this since it was getting kind of intractable (~60 commits -> 8 commits). Also, rebased everything to the latest main branch.

@vanandrew

Copy link
Copy Markdown
Author

Thought I give this a ping. Is there anything that you need from my end to get this merged in? @effigies @tsalo @mgxd

I think I've addressed all the current comments, but let me know if we need to discuss.

@effigies

Copy link
Copy Markdown
Member

Thanks. This is still on my to-do list. I think we're realistically going to need to focus on preserving old behavior and allow users to detect bugs in the new stuff, because I don't know that I have the bandwidth to review this as carefully as I would like.

@vanandrew

Copy link
Copy Markdown
Author

@effigies Alright. Then I'll work on re-implementing this as an optional separate pathway, while preserving the original code.

@effigies

Copy link
Copy Markdown
Member

@vanandrew Sorry, I didn't mean to imply that. I think keeping it as one pathway makes sense, and my review will just focus less on the correctness of the 4D branches and ensure the 3D branches are still doing what's expected.

Copilot AI balanced review requested due to automatic review settings September 29, 2026 22:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

MEDIC has confirmed workflow-construction and reporting failures, and its dependency packaging contradicts the stated opt-in design.

Review effort: Balanced
Findings: 7 High severity · 6 Medium severity

Open (13)
What changed in this PR

This PR adds MEDIC fieldmap estimation and per-volume distortion correction to sdcflows for multi-echo magnitude and phase data.

Changes:

  • Adds MEDIC detection, warpkit interfaces, and a fieldmap estimation workflow.
  • Adds 4D fieldmap resampling and connects it to derivative and report workflows.
  • Adds tests and CI fixtures; the current packaging makes warpkit a required dependency rather than the described opt-in extra.
File Description
sdcflows/​workflows/​tests/​test_outputs.py Checks 4D fieldmap merge configuration.
sdcflows/​workflows/​outputs.py Allows 4D fieldmaps in derivative output.
sdcflows/​workflows/​fit/​tests/​test_medic.py Tests MEDIC construction and metadata handling.
sdcflows/​workflows/​fit/​medic.py Builds the MEDIC estimation workflow.
sdcflows/​workflows/​base.py Connects dynamic estimators to preprocessing outputs.
sdcflows/​workflows/​apply/​tests/​test_dynamic.py Tests dynamic apply construction and resampling.
sdcflows/​workflows/​apply/​dynamic.py Adds the per-volume correction workflow.
sdcflows/​utils/​wrangler.py Discovers and orders MEDIC estimators.
sdcflows/​utils/​tests/​test_wrangler.py Tests MEDIC discovery and opt-out behavior.
sdcflows/​transform.py Supports per-frame fieldmaps in resampling.
sdcflows/​tests/​test_fieldmaps.py Tests MEDIC estimator validation.
sdcflows/​interfaces/​warpkit.py Wraps warpkit’s estimation stages.
sdcflows/​interfaces/​tests/​test_warpkit.py Checks warpkit interface specifications.
sdcflows/​fieldmaps.py Adds the MEDIC estimator type and workflow dispatch.
sdcflows/​conftest.py Provides MEDIC test fixtures.
sdcflows/​config.py Adds MEDIC opt-out configuration.
sdcflows/​cli/​parser.py Adds the --no-medic option.
sdcflows/​cli/​main.py Passes the option through dry-run discovery.
pyproject.toml Adds warpkit as a dependency.
.zenodo.json Adds contributor metadata.
.github/​workflows/​build-test-publish.yml Fetches MEDIC test data in CI.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread sdcflows/cli/main.py
Comment thread sdcflows/fieldmaps.py
Comment thread sdcflows/fieldmaps.py
Comment thread sdcflows/transform.py
Comment thread sdcflows/workflows/apply/dynamic.py
Comment thread sdcflows/utils/wrangler.py
Comment thread sdcflows/utils/wrangler.py
Comment thread sdcflows/utils/wrangler.py
Comment thread sdcflows/workflows/fit/medic.py
Comment thread sdcflows/workflows/fit/medic.py
Generalize _resample_with_fieldmap so a per-volume (4D) fieldmap runs
through the same code path as a static 3D one, selecting the matching
frame for each volume. fieldmap_jacobian now takes the voxel shift map
directly instead of recomputing it, so the static and dynamic callers
share a single definition. Document the fmap_hz preconditions.
Add UnwrapPhase and ComputeFieldmap, wrapping warpkit's multi-echo phase
unwrapping and fieldmap computation. Both follow the nipype num_threads
convention.

warpkit becomes a core dependency (>= 1.5.0). It carries a Washington
University non-commercial license, so commercial use requires a separate
WUSTL OTM agreement.
Add EstimatorType.MEDIC together with the is_dynamic property that tells
per-volume fieldmaps apart from static ones. MEDIC estimation validates
its file set up front, rejecting incomplete magnitude/phase part sets and
single-echo input rather than failing deep inside the workflow.
Discover MEDIC estimators from B0FieldIdentifier and IntendedFor metadata
only, never from directory structure alone. Cover the magnitude side of
IntendedFor and de-duplicate estimators that both fields point at.

Add --no-medic to opt out of MEDIC discovery entirely.
Fit workflow chaining UnwrapPhase into ComputeFieldmap to produce a
per-volume fieldmap from complex multi-echo BOLD. fmap_ref comes from the
raw first-echo magnitude and fmap_mask from warpkit's masks by way of
init_dynamic_magnitude_wf, so dynamic estimators expose the same outputs
as static ones.
Apply workflow that unwarps a BOLD series volume by volume against its
per-volume fieldmap, with Jacobian intensity correction and the
phase-encoding direction sign forwarded through to the fieldmap
conversion. Resampling goes through nitransforms.
…rivatives

Gate the static-versus-dynamic branch in init_fmap_preproc_wf on
FieldmapEstimation.is_dynamic, and align the merges so non-MEDIC
estimators keep working alongside MEDIC ones.

The derivatives writer dismisses the task entity on dynamic ref/mask
sinks and routes the dynamic magnitude reference through the fieldmap
suffix.
Stage ds006926 and ds007637 (multi-echo magnitude + phase BOLD) in the
cache-test-data job and run the MEDIC end-to-end tests on the veryslow
lane. Free runner disk before the data cache is restored, since the added
datasets push the root disk past its limit.

Skip both datasets in conftest's layout auto-index -- indexing them
stalls collection. Add Andrew Van to .zenodo.json.
Matching counts let phase echoes 1/2 through alongside magnitude echoes
1/3, and get_workflow then sorted each part independently by EchoTime,
handing warpkit a mismatched pair. Require one phase and one magnitude
file per echo entity with matching EchoTime, and order the magnitudes by
their paired phase echo.
_unpack_metadata read TotalReadoutTime off the first echo, so inputs
carrying only EffectiveEchoSpacing passed the preprocessing timing check
and then failed with KeyError. Resolve each echo's readout time with
get_trt (honoring use_metadata_estimates and fallback_total_readout_time)
and reject echoes that disagree.
…pies

A pre-gridded fieldmap was only reoriented, so one on a different
lattice was silently sampled as if co-gridded with the EPI. Compare
shape and affine against the target and fail clearly on mismatch.

unwarp_parallel copied the full coordinate grid for every volume before
the semaphore was acquired, so a long series queued one copy per frame.
Copy inside the worker once the semaphore is held.
Only the identifier listing was session-filtered, so an identifier reused
across sessions pulled every session's files into one estimator. Apply
the filter to both lookups; the regex lookup is filtered after the fact
since a regex session label would also match as a substring.
init_sdcflows_wf ignored --no-medic and always connected fmap_coeff,
which MEDIC does not emit. Forward no_medic to find_estimators and skip
the coefficient sink for dynamic estimators, as init_fmap_preproc_wf
already does.

init_fmap_preproc_wf's outer in_<id> node overrode the raw sources
get_workflow set on MEDIC's inputnode with undefined values. Seed it from
the estimator for dynamic estimators, whose raw sources need no
caller-side preprocessing (PEPOLAR/ANAT keep their current contract).
FieldmapReportlet reduced a 4D fieldmap to one frame but plotted the
4D reference and indexed with the 4D mask, so MEDIC reports failed.
Select the same frame from a 4D reference, moving image and mask.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support dynamic distortion correction with DOCMA

5 participants