"fmap" pseudo-entity complicates logic for reconstructing fieldmap filenames
I'm not entirely sure that this is expected to work, but it was a bit surprising to me. One of the entities associated with the fmap nifti is "fmap": "epi". But getting files with that entity excludes the associated json sidecar. I expected the json sidecar to have the same entities as the nifti, but for the extension.
import tempfile
from pathlib import Path
import nibabel as nb
import numpy as np
import json
import bids
sub = "0"
description = {"BIDSVersion": "1.9.0", "Name": "test"}
with tempfile.TemporaryDirectory() as d:
bidsdir = Path(d)
(bidsdir / "dataset_description.json").write_text(json.dumps(description))
fmapdir = bidsdir / f"sub-{sub}" / "fmap"
fmapdir.mkdir(parents=True)
for direction in ["AP", "PA"]:
img = fmapdir / f"sub-{sub}_acq-dwib0_dir-{direction}_epi.nii.gz"
nb.nifti1.Nifti1Image(np.zeros((4, 4)), affine=np.eye(4)).to_filename(
img
)
sidecar_api = fmapdir / img.name.replace(".nii.gz", ".json")
ped = "i" if direction == "AP" else "i-"
sidecar_api.write_text(
json.dumps({"PhaseEncodingDirection": ped, "TotalReadoutTime": 1})
)
test_layout = bids.BIDSLayout(bidsdir)
img_file: bids.layout.models.BIDSImageFile = test_layout.get(
subject=sub, suffix="epi", extension=".nii.gz", direction="AP"
)[0]
# show bids file
print(f"{img_file=}")
entities = img_file.get_entities()
entities_with_extension = {
**{k: v for k, v in entities.items() if k != "extension"},
**{"extension": ".json"},
}
# confirm that the new entities has the .json ending
# (but what's that extra fmap: epi?)
print(f"{entities_with_extension=}")
files = test_layout.get(**entities_with_extension)
# this is empty?
print(f"{files=}")
files_without_fmap_entity = test_layout.get(
**{k: v for k, v in entities_with_extension.items() if k != "fmap"}
)
# seems like the issue was the extra fmap entity
print(f"{files_without_fmap_entity=}")
img_file=<BIDSImageFile filename='/var/folders/v_/kcpb096s1m3_37ctfd2sp2xm0000gn/T/tmpq0b7dk7z/sub-0/fmap/sub-0_acq-dwib0_dir-AP_epi.nii.gz'>
entities_with_extension={'acquisition': 'dwib0', 'datatype': 'fmap', 'direction': 'AP', 'fmap': 'epi', 'subject': '0', 'suffix': 'epi', 'extension': '.json'}
files=[]
files_without_fmap_entity=[<BIDSJSONFile filename='/var/folders/v_/kcpb096s1m3_37ctfd2sp2xm0000gn/T/tmpiwo5vuhu/sub-0/fmap/sub-0_acq-dwib0_dir-AP_epi.json'>]
bids.__version__
'0.16.5'
If all you want to do is get the same file but .json, then I think bidsfile.path.replace(file.entities['extension'], '.json') will do the trick.
The entities are a bit of a mess, due to the conflation of all path components (and, as you note, fmap) in entities.
https://github.com/bids-standard/pybids/blob/d442c8b28ac179a94b1c02080a6dc10b61541c93/bids/layout/config/bids.json#L104-L107
It seems unlikely that anybody's using that undocumented "entity", that is just the same as suffix for fieldmaps, but removing it properly would take a deprecation cycle. Feel free to add a config option:
https://github.com/bids-standard/pybids/blob/master/bids/config.py
There's not a lot of churn, so the deprecation is likely to take a while, but there's no reason we can't start it.