I am writing nipype wrappers for my C++ tools. I have hit a problem that one particular tool of mine generates different numbers of output files (with different names) depending on the input parameters. This doesn’t seem to be something that can be easily supported in nipype?
I tried creating the OutputSpec in __init__(), but this doesn’t work because the cleaning stage of a workflow uses the OutputSpec as defined by the class, not the particular instance of the class.
PS - Is Neurostars the correct place for a question like this? The nipype lists here, gitter, the mailing list and Slack as venues. I picked the one that seemed to have highest traffic.
PPS - Why is the Brainhack Slack restricted to certain universities only? I can’t sign-up with my @kcl.ac.uk address.
With a DynamicTraitedSpec you can add new traits at any given time. This example shows how to add new output traits with names listed by the input fields.
1 - TraitedSpec instead of DynamicTraitedSpec gave
traits.trait_errors.TraitError: Cannot set the undefined 'DS_A' attribute of a 'LorentzianOutputSpec' object.
2 - Not adding the attributes during __init__:
Exception: Some connections were not found
Module lorentz has no output called DS_A
3 - Not adding the outputs in _list_outputs() gave:
File "/Users/Tobias/anaconda3/lib/python3.6/site-packages/nipype/pipeline/engine/utils.py", line 1402, in clean_working_directory
output_files.extend(walk_outputs(outputdict[output]))
KeyError: 'DS_A'
I’m not surprised but 1 & 2, but having to both add the attributes and set the outputs seems counter-intuitive. I think the key issue is I can’t quite get my head round how the standard version of _list_outputs() works.
(By “generalize” I meant write a proper, flexible version with dynamic outputs instead of writing multiple fixed cases)
That is expected. enthought.traits does not allow you to set undefined traits (unless you use the DynamicTraitedSpec which has _disallow = False).
If you already know that the names of your outputs are at instantiation, then it is good to set them within __init__. You can do it later though, it should not be a problem (the example I sent you sets them during _run_interface).
This is also expected. The _list_outputs() idea was to avoid holding too big objects in memory, but it is counter-intuitive. That is right.
Generalization in that sense could be done using xml specifications of the command line that can generate corresponding nipype interfaces automatically. Slicer used to work that way.
Thanks again @oesteban, I have something working now. I needed to add a dynamic InputSpec as well which was a bit of work, but I have some lovely CEST maps now.
I have some other questions about controlling logging, but that’s for another day and another topic.