diff --git a/.circleci/config.yml b/.circleci/config.yml index de0d19b8..71418ad8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -211,11 +211,25 @@ jobs: printf "[execution]\nstop_on_first_crash = true\n" > /tmp/bcp/nipype.cfg echo "poll_sleep_duration = 0.01" >> /tmp/bcp/nipype.cfg echo "hash_method = content" >> /tmp/bcp/nipype.cfg + - run: + name: Get intermediate transforms + command: | + mkdir -p /tmp/pooch + cd /tmp/pooch + # Caching intermediate templates so no need to constantly fetch + XFM="from-MNI152NLin6Asym_to-MNIInfant+1_xfm.h5" + echo "Downloading $XFM" + curl -Lo "$XFM" https://osf.io/download/kx7ny + XFM="from-MNIInfant+1_to-MNI152NLin6Asym_xfm.h5" + echo "Downloading $XFM" + curl -Lo "$XFM" https://osf.io/download/7ge2b + - persist_to_workspace: root: /tmp paths: - fslicense - bcp/nipype.cfg + - pooch/* test_pytest: !!merge <<: *machine_defaults @@ -334,7 +348,8 @@ jobs: --nthreads 4 -vv --age-months 2 --sloppy \ --surface-recon-method infantfs \ --derivatives precomputed=/tmp/data/${DATASET}/derivatives/bibsnet \ - --output-layout bids --anat-only + --output-layout bids --anat-only \ + --pooch-cache-dir /tmp/pooch - run: name: Checking outputs of anatomical nibabies run command: | @@ -360,10 +375,11 @@ jobs: --skull-strip-template UNCInfant:cohort-1 \ --output-spaces MNIInfant:cohort-1 func \ --sloppy --write-graph --mem-mb 14000 \ - --nthreads 4 -vv --age-months 2 --sloppy \ + --nthreads 4 -vv --age-months 2 \ --surface-recon-method infantfs \ --derivatives precomputed=/tmp/data/${DATASET}/derivatives/bibsnet \ - --output-layout bids + --output-layout bids \ + --pooch-cache-dir /tmp/pooch - run: name: Checking outputs of full nibabies run command: | @@ -393,7 +409,7 @@ jobs: cp -r /tmp/data/${DATASET}/derivatives/bibsnet /tmp/data/${DATASET}-t2only/derivatives - run: - name: Run nibabies single anatomical workflow + name: Run nibabies single anatomical workflow (T2w only) no_output_timeout: 1h command: | mkdir -p /tmp/data/${DATASET}-t2only /tmp/${DATASET}/derivatives/nibabies-t2only /tmp/${DATASET}/work-t2only @@ -406,10 +422,11 @@ jobs: --skull-strip-template UNCInfant:cohort-1 \ --output-spaces MNIInfant:cohort-1 func \ --sloppy --write-graph --mem-mb 14000 \ - --nthreads 4 -vv --age-months 2 --sloppy \ + --nthreads 4 -vv --age-months 2 \ --surface-recon-method infantfs \ --derivatives precomputed=/tmp/data/${DATASET}-t2only/derivatives/bibsnet \ - --output-layout bids --anat-only --cifti-output + --output-layout bids --anat-only --cifti-output \ + --pooch-cache-dir /tmp/pooch - run: name: Checking outputs of T2-only nibabies anat command: | diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 52c07215..f6528131 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -31,7 +31,7 @@ jobs: dependencies: [ 'latest', 'pre' ] include: - os: ubuntu-latest - python-version: '3.13' + python-version: '3.12' dependencies: 'min' env: diff --git a/env.yml b/env.yml index f92db278..b62b48fb 100644 --- a/env.yml +++ b/env.yml @@ -4,7 +4,7 @@ channels: - conda-forge # Update this ~yearly; last updated June 12 2025 dependencies: -- python=3.13 +- python=3.12 # Needed for svgo and bids-validator; consider moving to deno - nodejs=20 # Intel Math Kernel Library for numpy diff --git a/nibabies/cli/parser.py b/nibabies/cli/parser.py index 36647bb5..e66b92cd 100644 --- a/nibabies/cli/parser.py +++ b/nibabies/cli/parser.py @@ -15,7 +15,12 @@ def _build_parser(): """Build parser object.""" - from argparse import Action, ArgumentDefaultsHelpFormatter, ArgumentParser + from argparse import ( + Action, + ArgumentDefaultsHelpFormatter, + ArgumentParser, + BooleanOptionalAction, + ) from functools import partial from pathlib import Path @@ -759,7 +764,8 @@ def _str_none(val): ) g_baby.add_argument( '--multi-step-reg', - action='store_true', + action=BooleanOptionalAction, + default=True, help='For certain adult templates (MNI152NLin6Asym), perform two step ' 'registrations (native -> MNIInfant -> template) and concatenate into a single xfm', ) @@ -773,6 +779,11 @@ def parse_args(args=None, namespace=None): parser = _build_parser() opts = parser.parse_args(args, namespace) + if opts.sloppy: + config.loggers.cli.warning('Sloppy mode enabled: expect low-quality results') + # disable multi-step registration as it expects a warp + opts.multi_step_reg = False + if opts.config_file: skip = {} if opts.reports_only else {'execution': ('run_uuid',)} config.load(opts.config_file, skip=skip) diff --git a/nibabies/config.py b/nibabies/config.py index 08154068..cab424cd 100644 --- a/nibabies/config.py +++ b/nibabies/config.py @@ -578,7 +578,7 @@ class workflow(_Config): """Run FreeSurfer ``recon-all`` with the ``-logitudinal`` flag.""" medial_surface_nan = None """Fill medial surface with :abbr:`NaNs (not-a-number)` when sampling.""" - multi_step_reg = False + multi_step_reg = True """Perform multiple registrations (native -> MNIInfant -> template) and concatenate into a single transform""" norm_csf = False diff --git a/nibabies/utils/tests/test_bids.py b/nibabies/utils/tests/test_bids.py index a4e86259..c833c1c9 100644 --- a/nibabies/utils/tests/test_bids.py +++ b/nibabies/utils/tests/test_bids.py @@ -78,5 +78,5 @@ def test_get_age_from_tsv_warning(tmp_path): dual_participants = {'participant_id': ['sub-1', 'sub-2', 'sub-2']} create_tsv({**dual_participants, **age_months}, tsv_file) - with pytest.warns(UserWarning): + with pytest.warns(UserWarning, match='Multiple matches for participant_id:sub-2'): _get_age_from_tsv(tsv_file, 'participant_id', 'sub-2') diff --git a/nibabies/workflows/tests/test_base.py b/nibabies/workflows/tests/test_base.py index 7da27b86..678f248f 100644 --- a/nibabies/workflows/tests/test_base.py +++ b/nibabies/workflows/tests/test_base.py @@ -138,7 +138,7 @@ def _make_params( ignore: list[str] = None, bids_filters: dict = None, norm_csf: bool = False, - multi_step_reg: bool = False, + multi_step_reg: bool = True, ): if ignore is None: ignore = [] @@ -219,6 +219,7 @@ def _make_params( _make_params(bids_filters={'sbref': {'suffix': 'sbref'}}), _make_params(norm_csf=True), _make_params(multi_step_reg=True), + _make_params(multi_step_reg=False), ], ) def test_init_nibabies_wf( diff --git a/requirements.txt b/requirements.txt index 6f60ca49..bc1df650 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,13 +28,13 @@ bidsschematools==1.0.10 # via bids-validator bokeh==3.6.3 # via tedana -boto3==1.38.35 +boto3==1.38.36 # via datalad -botocore==1.38.35 +botocore==1.38.36 # via # boto3 # s3transfer -certifi==2025.4.26 +certifi==2025.6.15 # via requests chardet==5.2.0 # via datalad @@ -74,7 +74,7 @@ fasteners==0.19 # via datalad filelock==3.18.0 # via nipype -fonttools==4.58.2 +fonttools==4.58.4 # via matplotlib formulaic==1.1.1 # via pybids @@ -165,7 +165,7 @@ more-itertools==10.7.0 # datalad-next # jaraco-classes # jaraco-functools -msgpack==1.1.0 +msgpack==1.1.1 # via datalad networkx==3.5 # via @@ -385,7 +385,7 @@ seaborn==0.13.2 # nireports # niworkflows # tedana -simpleitk==2.5.0 +simpleitk==2.5.2 # via nibabies (pyproject.toml) simplejson==3.20.1 # via nipype diff --git a/wrapper/src/nibabies_wrapper/__main__.py b/wrapper/src/nibabies_wrapper/__main__.py index 6b459581..a0d46121 100755 --- a/wrapper/src/nibabies_wrapper/__main__.py +++ b/wrapper/src/nibabies_wrapper/__main__.py @@ -29,7 +29,7 @@ MISSING = """ Image '{}' is missing Would you like to download? [Y/n] """ -PKG_PATH = '/opt/conda/envs/nibabies/lib/python3.13/site-packages' +PKG_PATH = '/opt/conda/envs/nibabies/lib/python3.12/site-packages' TF_TEMPLATES = ( 'MNI152Lin', 'MNI152NLin2009cAsym', @@ -187,7 +187,7 @@ def set_version(self): self.add_envvar((version_env, version)) def add_envvar(self, envtuple): - """Set an environmental variable + """Set an environment variable Inputs ------ @@ -197,7 +197,7 @@ def add_envvar(self, envtuple): env = '='.join(envtuple) self.add_cmd(['-e', env]) elif self.service == 'singularity': - # singularity will transfer over environmental variables + # singularity will transfer over environment variables # with the prefix: SINGULARITYENV_ envvar, value = envtuple envvar = 'SINGULARITYENV_' + envvar @@ -476,6 +476,12 @@ def _is_file(path, parser): type=os.path.abspath, help='Filter file', ) + g_wrap.add_argument( + '--pooch-cache-dir', + metavar='DIR', + type=os.path.abspath, + help='Directory to serve as cache for pooch files' + ) # Developer patch/shell options g_dev = parser.add_argument_group( @@ -643,6 +649,9 @@ def main(): if opts.deriv_filter_file: container.add_mount(opts.deriv_filter_file, '/opt/derivative_filters.json') unknown_args.extend(['--deriv-filter-file', '/opt/derivative_filters.json']) + if opts.pooch_cache_dir: + container.add_mount(opts.pooch_cache_dir, '/tmp/pooch_cache', read_only=False) + container.add_envvar(('NIBABIES_POOCH_DIR', '/tmp/pooch_cache')) # Patch derivatives for searching if opts.derivatives: deriv_args = ['--derivatives']