Skip to content

Rules For Restructuring Code

James Kent edited this page Feb 13, 2017 · 25 revisions

Introduction

The re-writing will probably be done in phases to make the code more modular.Conceptually, we are starting with a novel and eventually we want to work towards a dictionary. There are a few guidelines we can follow to make this process easier/manageable.

Guidelines

#1: Functions

We are going to fit most the code into functions. The code as it stands does have some nice demarcations indicating when a certain process is occurring. For example, I pulled this from lines 524-535 in removeNuisanceRegressor.sh

###### FEAT registration correction ########################################
echo "...Fixing FEAT registration QC images."

 #http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FEAT/FAQ
   #ss: "How can I insert a custom registration into a FEAT analysis?"

regDir=${nuisancefeat}/reg

#Remove all FEAT files (after backup), repopulate with proper files
cp -r $regDir ${nuisancefeat}/regORIG
rm -f $regDir/*

The original creators gave helpful headers such as this one, "FEAT registration correction", to know how the code is naturally split up. For the example in this wiki, we can call this function regFix().

After giving the function a name, the next step is to find what files are called into this block of code. Here is the full block of code for our unstructured regFix() where I marked the input files/directories with (INFI)

###### FEAT registration correction ########################################

echo "...Fixing FEAT registration QC images."

  #http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FEAT/FAQ
    #ss: "How can I **insert** a custom registration into a FEAT analysis?"

(INFI)regDir=$indir/${nuisancefeat}/reg

#Remove all FEAT files (after backup), repopulate with proper files
cp -r $regDir $indir/${nuisancefeat}/regORIG
rm -rf $regDir

##Copy over appropriate reg directory from melodic.ica or preproc.feat processing
  #If Melodic was run, copy over that version of the registration, otherwise use the portion from preprocessing
  if [[ (INFI)$melFlag == 1 ]]; then
    ##Melodic was used (-P 2)
      #Copy over "melodic" registration directory
    cp -r (INFI)$indir/${melodicfeat}/reg $indir/${nuisancefeat}
  
  else
    ##Melodic was not used (-P 2a)
      #Copy over "preproc" registration directory
    cp -r (INFI)$indir/${preprocfeat}/reg $indir/${nuisancefeat}      
  fi

#Backup original design file
cp (INFI)$indir/${nuisancefeat}/design.fsf $indir/${nuisancefeat}/designORIG.fsf

After looking at the files and directories the code presumes exists, we can determine what the inputs to our function regFix are going to be. One big one appears to be the directory where we want the registration to be fixed, so we want $indir/${nuisancefeat} to be represented as an input directory, such as featdir. It also appears we are copying a directory from another feat registration, so that would also be an important input, we can name it inreg. The other input file design.fsf, I will assume exists and is observable from within featdir. Additionally, in order to make the function more general (e.g. more applicable in multiple scenarios), I've decided that $melFlag is something that is better handled in the main section of the script (the non-function portion). Convince yourself this is true or contact me to disagree.

Now that we know what's going in to our function, we can begin to write some code.

function regFix() {
local featdir=$1
local inreg=$2
....code to be written
}

Now it's time to see what our function is outputting/doing/producing. I'm going to paste the original code again, except this time I'm going to mark places that files/directories are being created with a (OUTFI).

 ###### FEAT registration correction ########################################

echo "...Fixing FEAT registration QC images."

  #http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FEAT/FAQ
    #ss: "How can I insert a custom registration into a FEAT analysis?"

regDir=$indir/${nuisancefeat}/reg

#Remove all FEAT files (after backup), repopulate with proper files
cp -r $regDir (OUTFI)$indir/${nuisancefeat}/regORIG
rm -rf $regDir

##Copy over appropriate reg directory from melodic.ica or preproc.feat processing
  #If Melodic was run, copy over that version of the registration, otherwise use the portion from preprocessing
  if [[ $melFlag == 1 ]]; then
    ##Melodic was used (-P 2)
      #Copy over "melodic" registration directory
    cp -r $indir/${melodicfeat}/reg (OUTFI)$indir/${nuisancefeat}
  
  else
    ##Melodic was not used (-P 2a)
      #Copy over "preproc" registration directory
    cp -r $indir/${preprocfeat}/reg (OUTFI)$indir/${nuisancefeat}      
  fi

#Backup original design file
cp $indir/${nuisancefeat}/design.fsf (OUTFI)$indir/${nuisancefeat}/designORIG.fsf

The output is the reg directory populated with the correct registration files, the designORIG.fsf, and the original registration directory regORIG. Luckily all these files are under the input directory featdir making the creation of these files a relatively easy code rewrite. Thus the function would look like the following:

function regFix() {
local featdir=$1
local inreg=$2

echo "...Fixing FEAT registration QC images."

#backup the original registration into regORIG and destroy reg.
cp -r ${featdir}/reg ${featdir}/regORIG ||\
{ printf "copying the regORIG directory failed, exiting ${FUNCNAME}" && return 1 ;}

rm -rf ${featdir}/reg

#copy the preferred registration files from a user supplied regdir to the user supplied featdir
cp -r ${inreg} ${featdir} ||\
{ printf "copying the reg directory failed, exiting ${FUNCNAME}" && return 1 ;}

#Backup original design file
cp ${featdir}/design.fsf ${featdir}/designORIG.fsf ||\
{ printf "copying design.fsf failed, exiting ${FUNCNAME}" && return 1 ;}

echo "$FUNCNAME completed successfully" && return 0
}

Congrats! the function is made, and be called like this:

regFix "/subdir/func/nuisancereg.feat" "/subdir/func/preproc.feat/reg"

There may be some bits of code that you don't recognize, and that's okay, I'll cover that in another page about being a defensive programmer that loves giving informative error messages. More on that later.

There is one final step towards creating a function and this arguably the most important, creating the documentation. A whole page can be written about this too, but I'll try to be brief. There can be 8 bits of information useful to anyone wanting to use your function. They are as follows: function: the name of your function purpose: what does the function do at a high level (conceptual level)? input: what files,directories,flags do you pass into the function? preconditions: what does the function assume about the input you passed into it? output: what does the function produce/make? dependencies: what other software/functions does this function need to work properly? Used in: where can find this function being used?

With this in mind, we can take a stab at writing the documentation of the function we just created.

##################
#function: regFix
##################
#purpose: Copies the reg directory from one featdir to another.
##################
#preconditions: feat has been ran on the input directory, and that the gold standard registration exists
##################
#input (typical): /subj/func/nuisancereg.feat (a feat directory)
#                 /subj/func/preproc.feat/reg (a gold standard registration directory)
##################
#output:         /subj/func/nuisancereg.feat/regORIG
#                /subj/func/nuisancereg.feat/designORIG.fsf
#                /subj/func/nuisancereg.feat/reg/<whatever are the different files>
##################
#dependencies: None <besides what comes with standard bash>
##################
#Used in: MAIN <the main script that isn't a part of a function>
##################

Now you have some of the tools to begin restructuring the code to a function format! Please see dataPrep.sh and qualityCheck.sh for more examples.

Definition of how to fill out parameters

##################
#function: <function name>
##################
#purpose: <what specific task does this function complete?>
##################
#preconditions: <what about the inputs do we assume?>
##################
#input (typical): <what inputs does the function take in?>
##################
#output: <what files the function creates or what output it returns>
##################
#dependencies: <what software/other functions are required for this function to run>
##################
#Used in: <where is this function called? in another function or in the main portion of the script?>
##################

Template

##################
#function:
##################
#purpose:
##################
#preconditions:
##################
#input (typical): 
##################
#output:
##################
#dependencies: 
##################
#Used in: 
##################

Clone this wiki locally