Skip to content

Commit 1dc7ad0

Browse files
committed
Draft surface-volume combination parsing.
1 parent 1dc0327 commit 1dc7ad0

File tree

1 file changed

+75
-3
lines changed

1 file changed

+75
-3
lines changed

niworkflows/utils/spaces.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,25 @@ def __attrs_post_init__(self):
168168
if self.space.startswith("fs"):
169169
object.__setattr__(self, "dim", 2)
170170

171+
if "volspace" in self.spec:
172+
volspace = self.spec["volspace"]
173+
if (self.space in self._standard_spaces) and (volspace not in self._standard_spaces):
174+
raise ValueError(
175+
f"Surface space ({self.space}) is a standard space, "
176+
f"but volume space ({volspace}) is not. "
177+
"Mixing standard and non-standard spaces is not currently allowed."
178+
)
179+
elif (self.space not in self._standard_spaces) and (volspace in self._standard_spaces):
180+
raise ValueError(
181+
f"Surface space ({self.space}) is a non-standard space, "
182+
f"but volume space ({volspace}) is a standard space. "
183+
"Mixing standard and non-standard spaces is not currently allowed."
184+
)
185+
171186
if self.space in self._standard_spaces:
172187
object.__setattr__(self, "standard", True)
173188

189+
# Check that cohort is handled appropriately
174190
_cohorts = ["%s" % t for t in _tfapi.TF_LAYOUT.get_cohorts(template=self.space)]
175191
if "cohort" in self.spec:
176192
if not _cohorts:
@@ -191,6 +207,30 @@ def __attrs_post_init__(self):
191207
"Set a valid cohort selector from: %s." % (self.space, _cohorts)
192208
)
193209

210+
# Check that cohort is handled appropriately for the volume template if necessary
211+
if "volspace" in self.spec:
212+
_cohorts = [
213+
"%s" % t for t in _tfapi.TF_LAYOUT.get_cohorts(template=self.spec["volspace"])
214+
]
215+
if "volcohort" in self.spec:
216+
if not _cohorts:
217+
raise ValueError(
218+
'standard space "%s" does not accept a cohort '
219+
"specification." % self.spec["volspace"]
220+
)
221+
222+
if str(self.spec["volcohort"]) not in _cohorts:
223+
raise ValueError(
224+
'standard space "%s" does not contain any cohort '
225+
'named "%s".' % (self.spec["volspace"], self.spec["volcohort"])
226+
)
227+
elif _cohorts:
228+
_cohorts = ", ".join(['"cohort-%s"' % c for c in _cohorts])
229+
raise ValueError(
230+
'standard space "%s" is not fully defined.\n'
231+
"Set a valid cohort selector from: %s." % (self.spec["volspace"], _cohorts)
232+
)
233+
194234
@property
195235
def fullname(self):
196236
"""
@@ -205,9 +245,17 @@ def fullname(self):
205245
'MNIPediatricAsym:cohort-1'
206246
207247
"""
208-
if "cohort" not in self.spec:
209-
return self.space
210-
return "%s:cohort-%s" % (self.space, self.spec["cohort"])
248+
name = self.space
249+
250+
if "cohort" in self.spec:
251+
name += f":cohort-{self.spec['cohort']}"
252+
253+
if "volspace" in self.spec:
254+
name += f"::{self.spec['volspace']}"
255+
if "volcohort" in self.spec:
256+
name += f":cohort-{self.spec['volcohort']}"
257+
258+
return name
211259

212260
@property
213261
def legacyname(self):
@@ -330,13 +378,37 @@ def from_string(cls, value):
330378
Reference(space='MNIPediatricAsym', spec={'cohort': '6', 'res': '2'}),
331379
Reference(space='MNIPediatricAsym', spec={'cohort': '6', 'res': 'iso1.6mm'})]
332380
381+
>>> Reference.from_string(
382+
... "dhcpAsym:cohort-42:den-32k::dhcpVol:cohort-44:res-2"
383+
... ) # doctest: +NORMALIZE_WHITESPACE
384+
[Reference(space='dhcpAsym', spec={'cohort': '42', 'den': '32k', 'volspace': 'dhcpVol',
385+
'volcohort': '44', 'res': '2'})]
386+
333387
"""
388+
volume_value = None
389+
if "::" in value:
390+
# CIFTI definition with both surface and volume spaces defined
391+
value, volume_value = value.split("::")
392+
# We treat the surface space definition as the "primary" space
393+
_args = value.split(":")
394+
334395
_args = value.split(":")
335396
spec = defaultdict(list, {})
336397
for modifier in _args[1:]:
337398
mitems = modifier.split("-", 1)
338399
spec[mitems[0]].append(len(mitems) == 1 or mitems[1])
339400

401+
if volume_value:
402+
# Tack on the volume space definition to the surface space definition
403+
volume_args = volume_value.split(":")
404+
# There are two special entities to prevent overloading: volspace and volcohort
405+
spec["volspace"] = [volume_args[0]]
406+
for modifier in volume_args[1:]:
407+
mitems = modifier.split("-", 1)
408+
if mitems[0] == "cohort":
409+
mitems[0] = "volcohort"
410+
spec[mitems[0]].append(len(mitems) == 1 or mitems[1])
411+
340412
allspecs = _expand_entities(spec)
341413

342414
return [cls(_args[0], s) for s in allspecs]

0 commit comments

Comments
 (0)