This section addresses how you can extend aa functionality by writing your own modules.
If you do repetitive analyses using a point-and-click interface, it is very easy to slip up and make a small mistake. If you’re unlucky, there won’t be a record of the error you made, or there you won’t notice the record. I find for anything but a one-subject pilot experiment, it is much safer to right a script.
If you have some Matlab code to do some processing, then there is a minimal cost in turning this into an aa module. Once you’ve done this two or three times, you will probably find it quicker to write code within aa than to not use it, as it provides a number of useful functions for finding data. For example, to get the EPI images from a previous processing stage for subject 1 and session 3 you can use:
This is typically quicker than writing your own file filters and so on. What’s more, the next time you come to use the module on some different data, it will work as soon as you add it to the pipeline.
You must follow these rules
- In version 4, you should always get inputs using either aas_getinputs_bystream (for any image type) or aas_getimages_bystream (only for epis). You may, in exceptional circumstances, use file filtering on particular directories, but this should be very general, so that no matter what processing stages have been run previously (e.g., smoothing, or normalization) the script will pick up the right files.
- Within modules, you should always access settings with
aap.tasksettings.(modulename).[settingname] %<<NOT THIS
for two reasons. Most importantly, if there is more than one instance of (modulename) in a tasklist, you won’t know which one holds the settings for the current job. Second, aap.tasklist.currenttask can be customised by extraparameters in the tasklist.
- To loop over sessions, use
for sess=1:length(aap.acq_details.sessions) <<NOT THIS
as the user may select a subset of sessions to be executed for a given branch. Also, to find the first session, do
if (sess==aap.acq_details.selected_sessions(1)) %...
if (sess==1) <<NOT THIS %...
for the same reason
- In XML module headers it is more convenient to use
<aap> <tasklist> <currenttask> ... </currenttask> </tasklist> </aap>
<!-- NOT THIS--> <aap> <tasksettings> <aamod_modulename> <!-- .. --> </aamod_modulename> </tasksettings> <aap>
Both syntaxes work, but it is easier to change the name of a module in the former syntax because only the .xml filename must be changed, and not the .xml code itself.
Each module comprises two files: a Matlab (.m) file containing the code, and an XML wrapper (.xml). By default, these two files have the same name apart from their suffix (e.g., aamod_realign.m and aamod_realign.xml).
The XML wrapper file contains:
- basic descriptors for a module, such as the domain over which it should be executed (once-per-study, once-per-subject or once-per session)
- the input and output streams for the module
- any settings the module can take [optional]
An example with all of these parts is the file aamod_norm_write_meanepi.xml:
<aap> <tasklist> <currenttask domain='subject' mfile_alias='aamod_norm_write' desc='SPM normalisation write (stream mean EPI)' modality='MRI'> <qsub> <timeBase>0.1</timeBase> <memoryBase>0.2</memoryBase> </qsub> <permanenceofoutput>2</permanenceofoutput> <session>1</session> <inputstreams> <stream>normalisation_seg_sn</stream> <stream>meanepi</stream> </inputstreams> <outputstreams> <stream>meanepi</stream> </outputstreams> </currenttask> </tasklist> </aap>
Note the optional mfile_alias=’xxx’ attribute, which specifies the mfile that should be used to execute the code.
The code for an example recipe, from aap_tasklist_typical_fmri.xml is shown below. Items can be added to the end of any recipe in your user script with the aas_addtask command, or you may copy and modify the .xml tasklist to fit your particular analysis.
<?xml version="1.0" encoding="utf-8"?> <aap> <tasklist> <initialisation> <module><name>aamod_checkparameters</name></module> <module><name>aamod_evaluatesubjectnames</name></module> <module><name>aamod_study_init</name></module> <module><name>aamod_newsubj_init</name></module> </initialisation> <main> <module><name>aamod_autoidentifyseries_timtrio</name></module> <module><name>aamod_get_dicom_structural</name></module> <module><name>aamod_get_dicom_epi</name></module> <module><name>aamod_converttmaps</name></module> <module><name>aamod_copystructural</name></module> <module><name>aamod_convert_epis</name></module> <module><name>aamod_realign</name></module> <module><name>aamod_tsdiffana</name></module> <module><name>aamod_slicetiming</name></module> <module><name>aamod_coreg_noss</name></module> <module><name>aamod_norm_noss</name></module> <module><name>aamod_norm_write</name></module> <module><name>aamod_smooth</name></module> </main> </tasklist> </aap>
You can easily modify the Recipe you are using by making a copy of the tasklist and changing it according to your needs, by deleting modules and adding modules of your own. It’s as simple as adding some code like this to the recipe between the <main> and </main> sections. For instance:
In aa version 4, reordering modules is a simple as reordering the lines in the tasklist. aa will then reconnect the data flow from one module to the next. It is a good idea to check the dependencies displayed by aa when you run your user script, to check the data flow is as you had planned.
tobecompleted and epiprefix are no longer used – data flow is determined by the “inputstreams” and “outputstreams” in each modules .xml settings
You may wish to customise general aa and spm settings.
Each of these changes is quite quick and simple to do.
Identify the inputs and outputs of your module. Some of these are likely to correspond to existing stream types, others (typically the outputs) will be new stream types – take a look at the how streams work.
Add the <inputstreams></inputstreams> and <outputstreams></outputstreams> sections to your module. Why not copy an example?
Use the aas_getfiles_bystream or aas_getimages_bystream command. Don’t filter directly for files any more – this is bad style, as it restricts interoperability with inputs from other sources that may have different filenames. This generally makes your code simpler and tidier. For example:
Any outputs from the module will only be able to be used if they can be read in as a stream by a subsequent module. You must use the aas_desc_outputs command to describe them. Some examples are:
aap=aas_desc_outputs(aap,subjnum,'normalisation_seg_sn',subj.matname); aap=aas_desc_outputs(aap,subjnum,sessnum, 'epi',epioutputfiles);
A subject-level module can write outputs at an level; a subject-level module can write subject or session outputs; and a session-level module can only write out at the session level.
The Second Level Model calculates the second level model and contrasts for your entire study, allowing you to judge group activations. This module is essentially self-sufficient and does not usually need any modification to function, although if you are an advanced user, you might want to dig in deeper.
Each module requires an XML script to go with it, let’s take the example of a module called aamod_firslevel_model.m and its xml file aamod_firstlevel_model.xml:
<?xml version="1.0" encoding="utf-8"?> <aap> &lt;tasksettings&gt; &amp;lt;aamod_firstlevel_model domain=&amp;quot;subject&amp;quot; desc=&amp;quot;First level model&amp;quot; modality=&amp;quot;MRI&amp;quot;&amp;gt; &amp;amp;lt;firstlevelmasking&amp;amp;gt;1&amp;amp;lt;/firstlevelmasking&amp;amp;gt; &amp;amp;lt;includemovementpars&amp;amp;gt;1&amp;amp;lt;/includemovementpars&amp;amp;gt; &amp;lt;/aamod_firstlevel_model&amp;gt; &lt;/tasksettings&gt; </aap>
Importantly, its name must be identical to the name of your module (e.g.aamod_firstlevel_model). It calso contains information on the domain of the module (session/subject/study) and the modality (MRI/EEG/MEG) and also contains a number of default parameters within itself, in this case, firslevelmasking and includemovementpars. You can change all these values within the XML according to your needs.
analysisname='chromatic'; myextraparams.stats_suffix=analysisname; aap=aas_addtask(aap,['aamod_firstlevel_model_' analysisname],'swar','[previous]',myextraparams); aap=aas_addtask(aap,['aamod_firstlevel_contrasts_' analysisname],,'[previous]',myextraparams); aap=aas_addtask(aap,['aamod_secondlevel_model_' analysisname] ,,'[previous]',myextraparams);
Here we choose to name the analysis chromatic, and we make it call aamod_firstlevel_model_chromatic, etc. Therefore, make sure both your .m and .xml files are called appropriately, and that the name within the xml file is set correctly. Happy modelling!