Esempio n. 1
0
def func_ttest(subj_list, out_dir, deriv_dir, sess, strA, strB, phase):

    # set up ETAC script
    list_A = []
    list_B = []
    for subj in subj_list:
        list_A.append(subj)
        list_A.append(
            os.path.join(deriv_dir, subj, sess,
                         f"{phase}_tentAvg_{strA}+tlrc"))
        list_B.append(subj)
        list_B.append(
            os.path.join(deriv_dir, subj, sess,
                         f"{phase}_tentAvg_{strB}+tlrc"))

    h_cmd = f"""
        module load afni-20.2.06
        cd {out_dir}

        3dttest++ \\
            -paired \\
            -mask Group_GM_intersect_mask+tlrc \\
            -prefix {phase}_{strA}-{strB}_tt \\
            -setA A {" ".join(list_A)} \\
            -setB B {" ".join(list_B)}
    """
    func_sbatch(h_cmd, 2, 4, 4, "vCATtt", out_dir)
Esempio n. 2
0
def func_etac(subj_list, out_dir, deriv_dir, sess, strA, strB, phase):

    # set up ETAC script
    list_A = []
    list_B = []
    for subj in subj_list:
        list_A.append(subj)
        list_A.append(
            os.path.join(deriv_dir, subj, sess,
                         f"{phase}_tentAvg_{strA}+tlrc"))
        list_B.append(subj)
        list_B.append(
            os.path.join(deriv_dir, subj, sess,
                         f"{phase}_tentAvg_{strB}+tlrc"))

    h_cmd = f"""
        module load afni-20.2.06
        cd {out_dir}

        # -ETAC_blur 4 6 8
        3dttest++ \\
            -paired \\
            -mask Group_GM_intersect_mask+tlrc \\
            -prefix {phase}_{strA}-{strB} \\
            -prefix_clustsim {phase}_{strA}-{strB}_clustsim \\
            -ETAC \\
            -ETAC_opt NN=2:sid=2:hpow=0:pthr=0.01,0.005,0.001:name=NN2 \\
            -setA A {" ".join(list_A)} \\
            -setB B {" ".join(list_B)}

        3dcopy {phase}_{strA}-{strB}_clustsim.NN2.ETACmask.global.2sid.5perc.nii.gz \
            FINAL_{phase}_{strA}-{strB}+tlrc
    """
    func_sbatch(h_cmd, 40, 6, 10, "vCATetac", out_dir)
Esempio n. 3
0
def func_clean_volreg(work_dir, phase_list, subj_num):
    """
    Step 7: Clean volreg data

    1) Censor potentially bad volumes to avoid biasing scale step.

    Note: This rarely has an effect.
    """

    # Determine minimum value, make mask
    #   beware the jabberwocky i.e. expanding braces in 3dMean
    for phase in phase_list:
        epi_list = func_epi_list(phase, work_dir)
        if not os.path.exists(
                os.path.join(work_dir, f"{phase}_minVal_mask+tlrc.HEAD")):
            h_cmd = f"""
                cd {work_dir}

                3dMean \
                    -datum short \
                    -prefix tmp_mean_{phase} \
                    tmp_run-{{1..{len(epi_list)}}}_{phase}_min+tlrc

                3dcalc \
                    -a tmp_mean_{phase}+tlrc \
                    -expr 'step(a-0.999)' \
                    -prefix {phase}_minVal_mask
            """
            func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}min", work_dir)

        # make clean data
        for run in epi_list:
            if not os.path.exists(
                    os.path.join(work_dir, f"{run}_volreg_clean+tlrc.HEAD")):
                h_cmd = f"""
                    cd {work_dir}
                    3dcalc \
                        -a {run}_warp+tlrc \
                        -b {phase}_minVal_mask+tlrc \
                        -expr 'a*b' \
                        -prefix {run}_volreg_clean
                """
                func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}cle", work_dir)
Esempio n. 4
0
def func_register(atlas, work_dir, subj_num):
    """
    Step 5: Calculate normalization

    1) This step will perform the rigid alignments of T1->EPI
        and non-linear diffeomorphic of T1->Template.
    """

    if not os.path.exists(os.path.join(work_dir, "struct_ns+tlrc.HEAD")):
        h_cmd = f"""
            cd {work_dir}

            align_epi_anat.py \
                -anat2epi \
                -anat struct+orig \
                -save_skullstrip \
                -suffix _al_junk \
                -epi epi_vrBase+orig \
                -epi_base 0 \
                -epi_strip 3dAutomask \
                -cost lpc+ZZ \
                -giant_move \
                -check_flip \
                -volreg off \
                -tshift off

            auto_warp.py \
                -base {atlas} \
                -input struct_ns+orig \
                -skull_strip_input no

            3dbucket \
                -DAFNI_NIFTI_VIEW=tlrc \
                -prefix struct_ns \
                awpy/struct_ns.aw.nii*

            cp awpy/anat.un.aff.Xat.1D .
            cp awpy/anat.un.aff.qw_WARP.nii .
        """
        func_sbatch(h_cmd, 1, 4, 4, f"{subj_num}dif", work_dir)
Esempio n. 5
0
def func_blur(work_dir, subj_num, blur_mult):
    """
    Step 8: Blur EPI data

    1) Blur kernel is size = blur_multiplier * voxel dim,
        rounded up to nearest int. FWHM.
    """

    # Blur
    epi_list = [
        x.split("_vol")[0] for x in os.listdir(work_dir)
        if fnmatch.fnmatch(x, "*volreg_clean+tlrc.HEAD")
    ]

    for run in epi_list:
        if not os.path.exists(os.path.join(work_dir, f"{run}_blur+tlrc.HEAD")):

            # calc voxel dim i
            h_cmd = f"""
                module load afni-20.2.06
                3dinfo -di {work_dir}/{run}+orig
            """
            h_gs = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
            h_gs_out = h_gs.communicate()[0]
            grid_size = h_gs_out.decode("utf-8").strip()
            blur_size = math.ceil(blur_mult * float(grid_size))

            # do blur
            h_cmd = f"""
                cd {work_dir}
                3dmerge \
                    -1blur_fwhm {blur_size} \
                    -doall \
                    -prefix {run}_blur \
                    {run}_volreg_clean+tlrc
            """
            func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}blur", work_dir)
Esempio n. 6
0
def func_scale(work_dir, phase_list, subj_num):
    """
    Step 10: Scale data

    1) Data is scaled by mean signal
    """

    for phase in phase_list:
        epi_list = func_epi_list(phase, work_dir)
        for run in epi_list:
            if not os.path.exists(
                    os.path.join(work_dir, f"{run}_scale+tlrc.HEAD")):
                h_cmd = f"""
                    cd {work_dir}

                    3dTstat -prefix tmp_tstat_{run} {run}_blur+tlrc

                    3dcalc -a {run}_blur+tlrc \
                        -b tmp_tstat_{run}+tlrc \
                        -c {phase}_minVal_mask+tlrc \
                        -expr 'c * min(200, a/b*100)*step(a)*step(b)' \
                        -prefix {run}_scale
                """
                func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}scale", work_dir)
Esempio n. 7
0
def func_tiss_masks(work_dir, subj_num, atropos_dict, atropos_dir):
    """
    Step 9:  Make union and tissue masks

    1) Make a union mask, where sufficient signal exists for both T1w
        and T2*w at each voxel for analyses. Incorporated at the
        group-level analysis.

    2) Make tissue masks. The WM mask will be used later to derive
        nuissance regressors for the REML.
        Note: this references some custom masks, and is based in
            atropos rather than in AFNIs tiss seg protocol.
    """

    epi_list = [
        x.split("_vol")[0] for x in os.listdir(work_dir)
        if fnmatch.fnmatch(x, "*volreg_clean+tlrc.HEAD")
    ]

    # Make EPI-T1 union mask (mask_epi_anat)
    if not os.path.exists(os.path.join(work_dir, "mask_epi_anat+tlrc.HEAD")):

        for run in epi_list:
            if not os.path.exists(
                    os.path.join(work_dir, f"tmp_mask.{run}_blur+tlrc.HEAD")):
                h_cmd = f"""
                    module load afni-20.2.06
                    cd {work_dir}
                    3dAutomask -prefix tmp_mask.{run} {run}_blur+tlrc
                """
                h_mask = subprocess.Popen(h_cmd,
                                          shell=True,
                                          stdout=subprocess.PIPE)
                h_mask.wait()

        h_cmd = f"""
            cd {work_dir}

            3dmask_tool \
                -inputs tmp_mask.*+tlrc.HEAD \
                -union \
                -prefix tmp_mask_allRuns

            3dresample \
                -master tmp_mask_allRuns+tlrc \
                -input struct_ns+tlrc \
                -prefix tmp_anat_resamp

            3dmask_tool \
                -dilate_input 5 -5 \
                -fill_holes \
                -input tmp_anat_resamp+tlrc \
                -prefix tmp_mask_struct

            3dmask_tool \
                -input tmp_mask_allRuns+tlrc tmp_mask_struct+tlrc \
                -inter \
                -prefix mask_epi_anat

            3dABoverlap \
                -no_automask tmp_mask_allRuns+tlrc tmp_mask_struct+tlrc | \
                tee out.mask_ae_overlap.txt
        """
        func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}uni", work_dir)

    # Make tissue-class masks
    #   I like Atropos better than AFNI's way, so use those priors
    h_ref = f"{epi_list[0]}_blur+tlrc"

    for key in atropos_dict:
        h_tiss = atropos_dict[key]
        if not os.path.exists(
                os.path.join(work_dir,
                             f"final_mask_{h_tiss}_eroded+tlrc.HEAD")):
            h_cmd = f"""
                module load c3d-1.0.0-gcc-8.2.0
                cd {work_dir}

                c3d \
                    {atropos_dir}/Prior{key}.nii.gz \
                    -thresh 0.3 1 1 0 \
                    -o tmp_{h_tiss}_bin.nii.gz

                3dresample \
                    -master {h_ref} \
                    -rmode NN \
                    -input tmp_{h_tiss}_bin.nii.gz \
                    -prefix final_mask_{h_tiss}+tlrc

                3dmask_tool \
                    -input tmp_{h_tiss}_bin.nii.gz \
                    -dilate_input -1 \
                    -prefix tmp_mask_{h_tiss}_eroded

                3dresample \
                    -master {h_ref} \
                    -rmode NN \
                    -input tmp_mask_{h_tiss}_eroded+orig \
                    -prefix final_mask_{h_tiss}_eroded
            """
            func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}atr", work_dir)
Esempio n. 8
0
def func_volreg_warp(work_dir, phase_list, subj_num, blip_tog):
    """
    Step 6: Warp EPI data to template space

    1) This step will perform the rigid alignments of T1-EPI (A)
        EPI-EPI base volume (B), and non-linear diffeomorphic of
        T1-Template (C). Note blip distortion map (D).

    2) Together, then, we will have T1-EPI (A), EPI-EPI base volume (B),
        and non-linear diffeomorphic of T1-Template (C) and possibly
        blip distortion map (D) matrices.

    3) It will then concatenate these warp matrices, and warp EPI data from
        raw/native space to template space via W=A'+B+C+D. Thus, only one
        interpolation of the epi data occurs.
    """

    scan_dict = {}
    h_str = "_blip+orig" if blip_tog == 1 else "+orig"

    for phase in phase_list:
        h_list = [
            x.split(".")[0] for x in os.listdir(work_dir)
            if fnmatch.fnmatch(x, f"run-*{phase}{h_str}.HEAD")
        ]
        h_list.sort()
        scan_dict[phase] = h_list
    scan_list = flatten_list(list(scan_dict.values()))

    for h_run in scan_list:

        # get run str
        run = h_run.split("_blip")[0] if blip_tog == 1 else h_run.split("+")[0]

        # Calculate volreg for e/run
        if not os.path.exists(os.path.join(work_dir,
                                           f"mat.{run}.vr.aff12.1D")):
            h_cmd = f"""
                cd {work_dir}

                3dvolreg \
                    -verbose \
                    -zpad 1 \
                    -base epi_vrBase+orig \
                    -1Dfile dfile.{run}.1D \
                    -prefix {run}_volreg \
                    -cubic \
                    -1Dmatrix_save mat.{run}.vr.aff12.1D \
                    {h_run}
            """
            func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}vre", work_dir)

        # set up, warp EPI to template
        if not os.path.exists(os.path.join(work_dir, f"{run}_warp+tlrc.HEAD")):

            # get grid size
            h_cmd = f"""
                module load afni-20.2.06
                3dinfo -di {work_dir}/{run}+orig
            """
            h_gs = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
            h_gs_out = h_gs.communicate()[0]
            grid_size = h_gs_out.decode("utf-8").strip()

            # concatenate matrices
            h_cmd = f"""
                module load afni-20.2.06
                cd {work_dir}

                cat_matvec \
                    -ONELINE \
                    anat.un.aff.Xat.1D \
                    struct_al_junk_mat.aff12.1D -I \
                    mat.{run}.vr.aff12.1D > mat.{run}.warp.aff12.1D
            """
            h_cat = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
            h_cat.wait()

            # warp epi, mask into template space
            nwarp_list = [
                "anat.un.aff.qw_WARP.nii", f"mat.{run}.warp.aff12.1D"
            ]
            if blip_tog == 1:
                nwarp_list.append("blip_warp_For_WARP+orig")

            h_cmd = f"""
                cd {work_dir}

                3dNwarpApply \
                    -master struct_ns+tlrc \
                    -dxyz {grid_size} \
                    -source {run}+orig \
                    -nwarp '{" ".join(nwarp_list)}' \
                    -prefix {run}_warp
            """
            func_sbatch(h_cmd, 2, 4, 4, f"{subj_num}war", work_dir)

        # Update - don't waste computation time warping
        #   simple mask into template space. Just make
        #   mask from warped EPI data
        if not os.path.exists(
                os.path.join(work_dir, f"tmp_{run}_min+tlrc.HEAD")):
            h_cmd = f"""
                module load afni-20.2.06
                cd {work_dir}

                3dcalc \
                    -overwrite \
                    -a {run}_warp+tlrc \
                    -expr 1 \
                    -prefix tmp_{run}_mask

                3dTstat \
                    -min \
                    -prefix tmp_{run}_min \
                    tmp_{run}_mask+tlrc
            """
            h_mask = subprocess.Popen(h_cmd,
                                      shell=True,
                                      stdout=subprocess.PIPE)
            h_mask.wait()
Esempio n. 9
0
def func_fmap_corr(work_dir, subj_num, phase_list):
    """
    Step 3: Blip correct data

    1) Calculate median of AP, PA files
    2) Compute midpoint between media files
    3) Apply warp to de-distort (unwarp) EPI data

    Note: If acq = LR, fmap = RL:
        base = LR, source = RL
        -pmNAMES RL LR
        unwarp LR with LR_WARP
    """

    # create median datasets and masks
    for h_dir in ["AP", "PA"]:
        if not os.path.exists(
                os.path.join(work_dir, f"tmp_med_masked_{h_dir}+orig.HEAD")):
            h_cmd = f"""
                cd {work_dir}

                3dTstat \
                    -median \
                    -prefix tmp_med_{h_dir} \
                    blip_{h_dir}+orig

                3dAutomask \
                    -apply_prefix tmp_med_masked_{h_dir} \
                    tmp_med_{h_dir}+orig
            """
            func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}med", work_dir)

    # compute midpoint between fmaps
    if not os.path.exists(
            os.path.join(work_dir, "blip_warp_For_WARP+orig.HEAD")):
        h_cmd = f"""
            cd {work_dir}

            3dQwarp \
                -plusminus \
                -pmNAMES Rev For \
                -pblur 0.05 0.05 \
                -blur -1 -1 \
                -noweight \
                -minpatch 9 \
                -source tmp_med_masked_PA+orig \
                -base tmp_med_masked_AP+orig \
                -prefix blip_warp
        """
        func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}qwa", work_dir)

    # unwarp run data (de-distort), apply header
    for phase in phase_list:
        epi_list = func_epi_list(phase, work_dir)
        for run in epi_list:
            if not os.path.exists(
                    os.path.join(work_dir, f"{run}_blip+orig.HEAD")):
                h_cmd = f"""
                    cd {work_dir}

                    3dNwarpApply \
                        -quintic \
                        -nwarp blip_warp_For_WARP+orig \
                        -source {run}+orig \
                        -prefix {run}_blip

                    3drefit \
                        -atrcopy blip_AP+orig \
                        IJK_TO_DICOM_REAL \
                        {run}_blip+orig
                """
                func_sbatch(h_cmd, 1, 2, 4, f"{subj_num}nwar", work_dir)
Esempio n. 10
0
def func_job(work_dir, subj, ses, phase, decon_type, seed_dict, stim_dur):

    # # for testing
    # work_dir = "/scratch/madlab/nate_ppi/derivatives"
    # subj = "sub-1040"
    # ses = "ses-S1"
    # phase = "Study"
    # decon_type = "2GAM"
    # seed_dict = {"LHC": "-24 -12 -22"}
    # stim_dur = 2

    """
    Step 1: Clean Data

    Create "clean data" by removing effects of no interest
    (baseline regressors) from scaled data.
    """
    subj_dir = os.path.join(work_dir, subj, ses)
    subj_num = subj.split("-")[1]

    # get TR
    h_cmd = (
        f"module load afni-20.2.06 \n 3dinfo -tr {subj_dir}/run-1_{phase}_scale+tlrc"
    )
    h_tr = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
    len_tr = float(h_tr.communicate()[0].decode("utf-8").strip())

    # get proper brick length
    #   REML appends an extra brick because
    #   "reasons". Account for AFNIs random
    #   0-1 indexing
    h_cmd = f"module load afni-20.2.06 \n 3dinfo -nv {subj_dir}/{phase}_decon_cbucket_REML+tlrc"
    h_len = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
    len_wrong = h_len.communicate()[0].decode("utf-8").strip()
    len_right = int(len_wrong) - 2

    # list all scale files
    scale_list = [
        x.split(".")[0]
        for x in os.listdir(subj_dir)
        if fnmatch.fnmatch(x, f"*{phase}*scale+tlrc.HEAD")
    ]
    scale_list.sort()

    # make clean data
    if not os.path.exists(os.path.join(subj_dir, f"CleanData_{phase}+tlrc.HEAD")):

        # list undesirable sub-bricks (those starting with Run or mot)
        no_int = []
        with open(os.path.join(subj_dir, f"X.{phase}_decon.xmat.1D")) as f:
            h_file = f.readlines()
            for line in h_file:
                if line.__contains__("ColumnLabels"):
                    col_list = (
                        line.replace("#", "").split('"')[1].replace(" ", "").split(";")
                    )
                    for i, j in enumerate(col_list):
                        if fnmatch.fnmatch(j, "Run*") or fnmatch.fnmatch(j, "mot*"):
                            no_int.append(f"{str(i)}")

        # strip extra sub-brick, make clean data by removing
        #   effects of no interest from concatenated runs
        h_cmd = f"""
            cd {subj_dir}
            3dTcat -prefix tmp_{phase}_cbucket -tr {len_tr} "{phase}_decon_cbucket_REML+tlrc[0..{len_right}]"
            3dSynthesize -prefix tmp_effNoInt_{phase} -matrix X.{phase}_decon.xmat.1D \
                -cbucket tmp_{phase}_cbucket+tlrc -select {" ".join(no_int)} -cenfill nbhr
            3dTcat -prefix tmp_all_runs_{phase} -tr {len_tr} {" ".join(scale_list)}
            3dcalc -a tmp_all_runs_{phase}+tlrc -b tmp_effNoInt_{phase}+tlrc -expr 'a-b' -prefix CleanData_{phase}
        """
        func_sbatch(h_cmd, 1, 4, 1, f"{subj_num}cle", subj_dir)

    # %%
    """
    Step 2: Seed Time Series

    1. Make HRF model, and seed from coordinates.
    2. Extract timeseries from seed ROI.
    3. Deconvolve HRF from timeseries (solve RHS).
    4. Upsample.
    """
    # find smallest multiplier that returns int for resampling
    res_multiplier = 2
    status = True
    while status:
        if ((len_tr * res_multiplier) % 2) == 0:
            status = False
        else:
            res_multiplier += 1

    # set status
    resample_decision = "easy" if res_multiplier <= 32 else "hard"

    # make ideal HRF, use same model as deconvolution
    #   TENT not supported.
    if not os.path.exists(os.path.join(subj_dir, "HRF_model.1D")):

        if decon_type == "dmBLOCK":
            no_data = f"{14 + stim_dur} 1"
            hrf_model = "BLOCK(1,1)"
        elif decon_type == "GAM":
            no_data = f"{round((1 / len_tr) * 13)} {len_tr}"
            hrf_model = "GAM"
        elif decon_type == "2GAM":
            no_data = f"{round((1 / len_tr) * 19)} {len_tr}"
            hrf_model = "TWOGAMpw(4,5,0.2,12,7)"

        h_cmd = f"""
            cd {subj_dir}
            3dDeconvolve -polort -1 \
                -nodata {no_data} \
                -num_stimts 1 \
                -stim_times 1 1D:0 '{hrf_model}' \
                -x1D HRF_model.1D -x1D_stop
        """
        func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}hrf", subj_dir)

    # get seed TS, solve RHS
    for key in seed_dict:

        # make seed, get TS
        if not os.path.exists(os.path.join(subj_dir, f"Seed_{key}_neural_us.1D")):
            h_cmd = f"""
                cd {subj_dir}
                echo {seed_dict[key]} | 3dUndump -xyz \
                    -srad 3 -master CleanData_{phase}+tlrc \
                    -prefix Seed_{key} -
                3dmaskave -quiet -mask Seed_{key}+tlrc CleanData_{phase}+tlrc > Seed_{key}_orig.1D
            """
            func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}mksd", subj_dir)

        # solve RHS, then upsample
        #   I want to use -l2lasso, but I'm scared
        if not os.path.exists(os.path.join(subj_dir, f"Seed_{key}_neural_us.1D")):
            h_cmd = f"""
                cd {subj_dir}
                3dTfitter -RHS Seed_{key}_orig.1D -FALTUNG HRF_model.1D tmp.1D 012 0
                1dtranspose tmp.1D > Seed_{key}_neural.1D
            """
            func_sbatch(h_cmd, 8, 1, 4, f"{subj_num}flt", subj_dir)

            if resample_decision == "easy":
                h_cmd = f"""
                    module load afni-20.2.06
                    1dUpsample {res_multiplier} {subj_dir}/Seed_{key}_neural.1D \
                        > {subj_dir}/Seed_{key}_neural_us.1D
                """
                h_sp = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)

            elif resample_decision == "hard":

                seed_file = open(f"{subj_dir}/Seed_{key}_neural.1D", "r")
                h_ts = seed_file.readlines()
                seed_ts = [float(x.strip()) for x in h_ts]
                seed_us = func_upsample(seed_ts, len_tr, 1)

                with open(f"{subj_dir}/Seed_{key}_neural_us.1D", "w") as seed_out:
                    for value in seed_us:
                        seed_out.write("%s\n" % value)

    # %%
    """
    Step 3: Make Behavior Time Series

    1. Make behavior binary vectors from timing files.
    2. Upsample w/o interpolation.
    3. Extract behavior portions of seed neural timeseries (intx).
    4. Downsample intx via Bash.
    5. Convolve intx with HRF model.
    """
    # list of timing files
    time_dir = os.path.join(subj_dir, "timing_files")
    tf_list = [
        os.path.join(time_dir, x)
        for x in os.listdir(time_dir)
        if fnmatch.fnmatch(x, f"tf_{phase}*.txt")
    ]

    # list of run length in seconds
    run_len = []
    num_vol = []
    for i in scale_list:
        h_cmd = f"module load afni-20.2.06 \n 3dinfo -ntimes {subj_dir}/{i}"
        h_sp = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
        h_vol = int(h_sp.communicate()[0].decode("utf-8").strip())
        num_vol.append(h_vol)
        run_len.append(h_vol * len_tr)

    for tf_path in tf_list:
        # tf_path = tf_list[0]
        h_beh = tf_path.split(".")[0].split("_")[-1]

        # get upsampled behavior binary file
        if not os.path.exists(os.path.join(subj_dir, f"Beh_{h_beh}_us.1D")):
            h_cmd = f"""
                module load afni-20.2.06
                cd {subj_dir}
                timing_tool.py -timing {tf_path} -tr {len_tr} \
                    -stim_dur {stim_dur} -run_len {" ".join(map(str, run_len))} \
                    -min_frac 0.3 -timing_to_1D Beh_{h_beh}_bin.1D
            """
            # func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}{h_beh}", subj_dir)
            h_sp = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)
            (h_err, h_out) = h_sp.communicate()
            h_sp.wait()

            if resample_decision == "easy":
                h_cmd = f"""
                    module load afni-20.2.06
                    awk '{{for(j=0;j<{res_multiplier};j++)print}}' \
                        {subj_dir}/Beh_{h_beh}_bin.1D > {subj_dir}/Beh_{h_beh}_us.1D
                """
                h_sp = subprocess.Popen(h_cmd, shell=True, stdout=subprocess.PIPE)

            elif resample_decision == "hard":

                tf_file = open(f"{subj_dir}/Beh_{h_beh}_bin.1D", "r")
                h_tf = tf_file.readlines()
                tf_ts = [int(x.strip()) for x in h_tf]

                # Note: don't interpolate upsample
                tf_us = func_upsample(tf_ts, len_tr, 0)

                with open(f"{subj_dir}/Beh_{h_beh}_us.1D", "w") as tf_out:
                    for value in tf_us:
                        tf_out.write("%s\n" % value)

        # multiply beh bin file by clean neural to get intx term.
        #   downsample in bash, pad final vol
        for key in seed_dict:
            if not os.path.exists(
                os.path.join(subj_dir, f"Seed_{key}_{h_beh}_neural.1D")
            ):

                ds_factor = (
                    res_multiplier
                    if resample_decision == "easy"
                    else int(1000 * len_tr)
                )

                h_cmd = f"""
                    cd {subj_dir}
                    1deval -a Seed_{key}_neural_us.1D -b Beh_{h_beh}_us.1D \
                        -expr 'a*b' > Seed_{key}_{h_beh}_neural_us.1D
                    cat Seed_{key}_{h_beh}_neural_us.1D | \
                        awk -v n={ds_factor} 'NR%n==0' > Seed_{key}_{h_beh}_neural.1D && \
                        echo 0 >> Seed_{key}_{h_beh}_neural.1D
                """
                func_sbatch(h_cmd, 2, 1, 1, f"{subj_num}nts", subj_dir)

            # convolve seed beh neural (intx) with HRF
            if not os.path.exists(
                os.path.join(subj_dir, f"Final_{key}_{h_beh}_timeSeries.1D")
            ):
                h_cmd = f"""
                    cd {subj_dir}
                    waver -FILE {len_tr} HRF_model.1D \
                        -peak 1 -TR {len_tr} \
                        -input Seed_{key}_{h_beh}_neural.1D \
                        -numout {sum(num_vol)} > Final_{key}_{h_beh}_timeSeries.1D
                """
                func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}ds", subj_dir)

    # %%
    """
    Step 4: Rerun Deconvolution

    Same decon as before, add Seed and Behavior timeseries.
    """
    # Write decon script
    if not os.path.exists(os.path.join(subj_dir, f"X.{phase}_decon_ppi.xmat.1D")):

        # determine motion list
        mot_list = [
            x
            for x in os.listdir(subj_dir)
            if fnmatch.fnmatch(x, f"mot_demean_{phase}.*.1D")
        ]
        mot_list.sort()

        # make timing file dict
        tf_dict = {}
        for i in tf_list:
            tmp = i.split("_")[-1]
            beh = tmp.split(".")[0]
            tf_dict[beh] = i

        # make ppi dict
        ppi_dict = {}
        for key in seed_dict:
            ppi_dict[key] = f"Seed_{key}_orig.1D"
            for beh in tf_dict:
                ppi_dict[f"{key}_{beh}"] = f"Final_{key}_{beh}_timeSeries.1D"

        # write decon script, generate matrices and REML_cmd
        decon_script = os.path.join(subj_dir, f"ppi_decon_{phase}.sh")
        with open(decon_script, "w") as script:
            script.write(
                func_decon_ppi(
                    scale_list,
                    mot_list,
                    tf_dict,
                    f"censor_{phase}_combined.1D",
                    phase,
                    decon_type,
                    ppi_dict,
                )
            )

        # run decon script to generate matrices
        h_cmd = f"cd {subj_dir} \n source {decon_script}"
        func_sbatch(h_cmd, 1, 1, 1, f"{subj_num}dcn", subj_dir)

    # run REML
    if not os.path.exists(
        os.path.join(subj_dir, f"{phase}_decon_ppi_stats_REML+tlrc.HEAD")
    ):
        h_cmd = f"cd {subj_dir} \n tcsh -x {phase}_decon_ppi_stats.REML_cmd -dsort {phase}_WMe_rall+tlrc"
        func_sbatch(h_cmd, 10, 4, 6, f"{subj_num}rml", subj_dir)