Repository: dPys/PyNets (language: python)

Issue:
[TST] Minor test additions
Tiny parameter addition to test_gen_mask()


Discussion / hints:

Fix patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1af1dad5..de7a48c9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -269,9 +269,10 @@ jobs:
           no_output_timeout: 6h
           command: |
             set -e
+            TEST_FILES=$(circleci tests glob "tests/test_*.py" | circleci tests split --split-by=timings)
             mkdir test-results
             export PYTHONUNBUFFERED=1
-            python -m coverage run -p --rcfile=.coveragerc -m pytest -s --circleci-parallelize --verbose --junitxml="test-results/junit.xml" tests/test_*.py
+            python -m coverage run -p --rcfile=.coveragerc -m pytest -s --circleci-parallelize --verbose --junitxml="test-results/junit.xml" $TEST_FILES
             sudo chmod 777 -R /usr/lib/python3/dist-packages
       - persist_to_workspace:
           root: /tmp/src/pynets
diff --git a/.idea/PyNets.iml b/.idea/PyNets.iml
index d77539ff..98acace2 100644
--- a/.idea/PyNets.iml
+++ b/.idea/PyNets.iml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="PYTHON_MODULE" version="4">
   <component name="NewModuleRootManager">
-    <content url="file://$MODULE_DIR$" />
-    <orderEntry type="jdk" jdkName="Python 3.7 (PyNets)" jdkType="Python SDK" />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/venv" />
+    </content>
+    <orderEntry type="jdk" jdkName="Remote Python 3.8.10 (sftp://dpys@192.168.1.92:22/usr/bin/python)" jdkType="Python SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="R Skeletons" level="application" />
     <orderEntry type="library" name="R User Library" level="project" />
@@ -17,7 +19,4 @@
       </list>
     </option>
   </component>
-  <component name="TestRunnerService">
-    <option name="PROJECT_TEST_RUNNER" value="Unittests" />
-  </component>
 </module>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 08612ea0..63eb946a 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,7 +3,13 @@
   <component name="JavaScriptSettings">
     <option name="languageLevel" value="ES6" />
   </component>
-  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (PyNets)" project-jdk-type="Python SDK" />
+  <component name="ProjectPlainTextFileTypeManager">
+    <file url="file://$PROJECT_DIR$/licenses/AGPL-3.0.txt" />
+    <file url="file://$PROJECT_DIR$/licenses/PYNETS.txt" />
+    <file url="file://$PROJECT_DIR$/licenses/THIRDPARTY.txt" />
+    <file url="file://$PROJECT_DIR$/NOTICE" />
+  </component>
+  <component name="ProjectRootManager" version="2" project-jdk-name="Remote Python 3.8.10 (sftp://dpys@192.168.1.92:22/usr/bin/python)" project-jdk-type="Python SDK" />
   <component name="RMarkdownSettings">
     <option name="renderProfiles">
       <map>
diff --git a/.zenodo.json b/.zenodo.json
index 115d4cd4..529c1ee6 100644
--- a/.zenodo.json
+++ b/.zenodo.json
@@ -1,22 +1,11 @@
 {
   "title": "A Reproducible Workflow for Structural and Functional Connectome Ensemble Learning",
-  "description": "<p>PyNets harnesses the power of Nipype, Nilearn, Dipy, and Networkx packages to automatically generate graphical ensembles on a subject-by-subject basis, using any combination of graph-generating hyperparameters. PyNets utilities can be integrated with any existing preprocessing workflow, and a docker container is provided to facilitate complete reproducibility of executions.</p>",
-  "creators": [
-    {
-      "name": "Pisner, Derek",
-      "affiliation": "Department of Psychology, University of Texas at Austin, TX, USA",
-      "orcid": "0000-0002-1228-0201"
-    }
-  ],
+  "description": "<p>PyNets harnesses the flexibility of Nipype, Nilearn, Dipy, and Networkx packages to automatically generate graphical ensembles on a subject-by-subject basis, using any combination of graph-generating hyperparameters. PyNets utilities can be integrated with any existing preprocessing workflow, and a docker container is provided to facilitate complete reproducibility of executions.</p>",
   "maintainers": [
     {
       "name": "Pisner, Derek",
       "affiliation": "Department of Psychology, University of Texas at Austin, TX, USA",
       "orcid": "0000-0002-1228-0201"
-    },
-    {
-      "name": "Hammonds, Ryan",
-      "affiliation": "University of Texas at Austin, TX, USA"
     }
   ],
   "keywords": [
diff --git a/Dockerfile b/Dockerfile
index 1c93a962..ed95324f 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -174,11 +174,11 @@ RUN echo "FSLDIR=/usr/share/fsl/5.0" >> /home/neuro/.bashrc && \
 	gnupg \
 	g++ \
 	git-lfs \
-	libwebkit2gtk-* \
-	gnome-icon-theme \
-	libgtk-*-common \
-	libgtk*-common \
-	libjavascriptcoregtk-* \
+#	libwebkit2gtk-* \
+#	gnome-icon-theme \
+#	libgtk-*-common \
+#	libgtk*-common \
+#	libjavascriptcoregtk-* \
     && rm -rf /var/lib/{apt,dpkg,cache,log}/ \
     && conda clean -tipsy \
     && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
diff --git a/MANIFEST.in b/MANIFEST.in
index 5b433458..2f842ddb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,6 +4,7 @@ include LICENSE
 include README.rst
 include versioneer.py
 include pynets/_version.py
+include pynets/advanced.yaml
 include requirements.txt
 
 recursive-include * *.nii.gz
@@ -13,7 +14,7 @@ recursive-include * *.txt
 recursive-include * *.csv
 recursive-include pynets/cli *
 recursive-include pynets/core *
-recursive-include pynets/config *
+recursive-include pynets/runconfigs *
 recursive-include pynets/templates/labelcharts *
 recursive-include pynets/registration *
 recursive-include pynets/fmri *
@@ -23,6 +24,8 @@ recursive-include pynets/templates/rsnrefs *
 recursive-include pynets/dmri *
 recursive-include pynets/plotting *
 recursive-include pynets/stats *
+recursive-include pynets/stats/individual *
+recursive-include pynets/stats/group *
 recursive-exclude tests *
 recursive-exclude * *__pycache__
 recursive-exclude * *.py[co]
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 00000000..076a9504
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,41 @@
+PyNets®: A Reproducible Workflow for Structural and Functional Connectome
+Ensemble Learning
+
+Copyright (C) 2017 Derek Pisner
+dpisner@utexas.edu
+
+*Since its conception in 2017 this product was provided in open-source under a
+GNU GPL Version 2 (GPLv2) license. Beginning in December 2021, however, this
+product was approved for relicensing in open-source under a GNU Affero GPL
+Version 3 (AGPLv3) license, the terms of which can be found verbatim in
+licenses/AGPL-3.0.txt, and which are summarized briefly herein:
+
+    This product is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published
+    by the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This product is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+*This product includes software that depends on open-source libraries
+developed by the NiPy Community (https://nipy.org/).
+See `licenses/THIRDPARTY.txt` for details.
+
+*Portions of this product were developed while the author was a graduate
+student at the Department of Psychology at the University of Texas at Austin,
+Austin, TX, US. However, since no portions of this product were developed
+using any form of federal funding or assistance, the University of Texas at
+Austin, on behalf of the Board of Regents of the University of Texas System,
+has confirmed that it does not assert any ownership interest in this product,
+its embodying U.S. Patent 11,188,850, Application 16/372,367, or any patents
+or patent applications that claim priority to that application.
+
+*This product redistributes the versioneer Python package, which is
+Public domain source code.
+
+*This product is also distributed as a Docker container image.
+The bootstraping file for the image ("Dockerfile") is licensed
+under the MIT License.
diff --git a/licenses/AGPL-3.0.txt b/licenses/AGPL-3.0.txt
new file mode 100644
index 00000000..0ad25db4
--- /dev/null
+++ b/licenses/AGPL-3.0.txt
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published
+    by the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.
diff --git a/licenses/THIRDPARTY.txt b/licenses/THIRDPARTY.txt
new file mode 100644
index 00000000..f00d4956
--- /dev/null
+++ b/licenses/THIRDPARTY.txt
@@ -0,0 +1,119 @@
+===============================================================================
+                    REFERENCES TO THIRD-PARTY LICENSES
+===============================================================================
+
+A. Incorporated Brain Templates and Atlases
+===========================================
+
+    i. The MNI templates
+
+    Copyright (C) 1993-2004 Louis Collins, McConnell Brain Imaging Centre,
+    Montreal Neurological Institute, McGill University.
+
+    ----------------------------------------------------------------------------
+
+    ii. The Standard Space Atlases
+
+    The Harvard-Oxford, JHU, Juelich, atlases, whilst not being the property
+    of the Licensor, are released at the request of their owners.
+    For commercial use, please contact the primary coordinator for the
+    relevant atlas to receive permissions:
+
+    Harvard-Oxford: steve@fmrib.ox.ac.uk
+    JHU: susumu@mri.jhu.edu
+    Juelich: S.Eickhoff@fz-juelich.de
+
+    ----------------------------------------------------------------------------
+    iii. The Neuromorphometrics atlas
+
+    Maximum probability tissue labels derived from the ``MICCAI 2012 Grand
+    Challenge and Workshop on Multi-Atlas Labeling’’
+    (https://masi.vuse.vanderbilt.edu/workshop2012/index.php/
+    Challenge_Details). These data were released under the Creative Commons
+    Attribution Non-Commercial (CC BY-NC) with no end date. Users should
+    credit the MRI scans as originating from the OASIS project
+    (http://www.oasis-brains.org/) and the labeled data as “provided by
+    Neuromorphometrics, Inc. (http://Neuromorphometrics.com/) under academic
+    subscription”. These references should be included in all workshop and
+    final publications.
+
+B. Incorporated Software
+========================
+
+    i. nipype, boto3, requests
+
+    nipype: Copyright (c) 2009-2016, Nipype developers
+    boto3: Copyright 2013-2017 Amazon.com, Inc. or its affiliates
+    requests: Copyright 2019 Kenneth Reitz
+
+    Apache 2.0 License
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    ----------------------------------------------------------------------------
+
+    ii. nilearn, dipy, sphinxext, python-Louvain, scikit-image, scikit-learn,
+    networkx, seaborn, scipy, numpy, joblib, psutil, pandas, statsmodels, dill,
+    colorama, h5py
+
+    nilearn: Copyright (c) 2007 - 2015 Nilearn developers.
+    dipy: Copyright (c) 2008-2021, Dipy developers.
+    Sphinxext: Copyright (c) 2007-2015 by the Sphinx team.
+    python-Louvain: Copyright (c) 2009-2018, Thomas Aynaud <thomas.aynaud@lip6.fr>
+    scikit-image: Copyright (C) 2019, the scikit-image team
+    scikit-learn: Copyright (c) 2007-2021 The scikit-learn developers.
+    networkx: Copyright (C) 2004-2021, NetworkX Developers
+              Aric Hagberg <hagberg@lanl.gov>
+              Dan Schult <dschult@colgate.edu>
+              Pieter Swart <swart@lanl.gov>
+    seaborn: Copyright (c) 2012-2021, Michael L. Waskom
+    scipy: Copyright (c) 1999-2010 SciPy Developers <scipy-dev@scipy.org>
+    numpy: Copyright (c) 2005-2021, NumPy Developers.
+    joblib: Copyright (c) 2008-2021, The joblib developers.
+    psutil: Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola
+    pandas: Copyright (c) 2008-2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team.
+            Copyright (c) 2011-2021, Open source contributors.
+    statsmodels: Copyright (C) 2006, Jonathan E. Taylor
+                 Copyright (c) 2006-2008 Scipy Developers.
+                 Copyright (c) 2009-2018 statsmodels Developers.
+    dill: Copyright (c) 2004-2016 California Institute of Technology.
+          Copyright (c) 2016-2021 The Uncertainty Quantification Foundation.
+    colorama: Copyright (c) 2010 Jonathan Hartley
+    h5py: Copyright (c) 2008 Andrew Collette and contributors
+
+    BSD 3-Clause License
+    https://opensource.org/licenses/BSD-3-Clause
+
+    ----------------------------------------------------------------------------
+
+    iii. graspologic, mplcyberpunk, pybids, nibabel, sqlalchemy, six, pyyaml, urllib3
+
+    Graspologic: Copyright (c) Microsoft Corporation
+    mplcyberpunk: Copyright (c) 2020 Dominik Haitz
+    pybids: Copyright (c) 2015-2016, Ariel Rokem, The University of Washington eScience Institute.
+    pybids: Copyright (c) 2016--, PyBIDS developers
+    nibabel: Copyright (c) 2006--, Nibabel developers
+             Michael Hanke <michael.hanke@gmail.com>
+             Matthew Brett <matthew.brett@gmail.com>
+             Stephan Gerhard <git@unidesign.ch>
+             Christian Haselgrove <christian.haselgrove@umassmed.edu>
+             Jarrod Millman <jarrod.millman@gmail.com>
+             Yaroslav Halchenko <debian@onerussian.com>
+             Chris Markiewicz <effigies@gmail.com>
+    sqlalchemy: Copyright 2005-2021 SQLAlchemy authors and contributors
+    six: Copyright (c) 2010-2020 Benjamin Peterson
+    pyyaml: Copyright (c) 2017-2021 Ingy döt Net
+            Copyright (c) 2006-2016 Kirill Simonov
+    urllib3: Copyright (c) 2008-2020 Andrey Petrov and contributors
+
+    MIT License
+    https://opensource.org/licenses/MIT
+
+    ----------------------------------------------------------------------------
+
+    iv. matplotlib
+
+    matplotlib: Copyright (c) 2003-2013, John Hunter
+                Copyright (c) 2013--, Matplotlib Development Team
+
+    PSF License
+    https://opensource.org/licenses/Python-2.0
diff --git a/optional-requirements.txt b/optional-requirements.txt
index a89585f6..c2f651ec 100644
--- a/optional-requirements.txt
+++ b/optional-requirements.txt
@@ -3,4 +3,6 @@ pingouin>=0.3.7
 imbalanced-learn>=0.8.0
 awscli>=1.16.0
 skggm>=0.2.8
+ConfigParser>=3.7.4
+templateflow>=0.7.0
 git+https://github.com/dPys/multinetx.git@master
diff --git a/pynets/config/bids_config.json b/pynets/Initializers/bids_config.json
similarity index 100%
rename from pynets/config/bids_config.json
rename to pynets/Initializers/bids_config.json
diff --git a/pynets/config/bids_config_anatomical_atlases.json b/pynets/Initializers/bids_config_anatomical_atlases.json
similarity index 100%
rename from pynets/config/bids_config_anatomical_atlases.json
rename to pynets/Initializers/bids_config_anatomical_atlases.json
diff --git a/pynets/config/bids_config_bold.json b/pynets/Initializers/bids_config_bold.json
similarity index 100%
rename from pynets/config/bids_config_bold.json
rename to pynets/Initializers/bids_config_bold.json
diff --git a/pynets/config/bids_config_clustering.json b/pynets/Initializers/bids_config_clustering.json
similarity index 100%
rename from pynets/config/bids_config_clustering.json
rename to pynets/Initializers/bids_config_clustering.json
diff --git a/pynets/config/bids_config_dwi.json b/pynets/Initializers/bids_config_dwi.json
similarity index 100%
rename from pynets/config/bids_config_dwi.json
rename to pynets/Initializers/bids_config_dwi.json
diff --git a/pynets/config/bids_config_embed.json b/pynets/Initializers/bids_config_embed.json
similarity index 100%
rename from pynets/config/bids_config_embed.json
rename to pynets/Initializers/bids_config_embed.json
diff --git a/pynets/config/bids_config_rsns.json b/pynets/Initializers/bids_config_rsns.json
similarity index 100%
rename from pynets/config/bids_config_rsns.json
rename to pynets/Initializers/bids_config_rsns.json
diff --git a/pynets/config/cloud_config.json b/pynets/Initializers/cloud_config.json
similarity index 100%
rename from pynets/config/cloud_config.json
rename to pynets/Initializers/cloud_config.json
diff --git a/pynets/__init__.py b/pynets/__init__.py
index 4cd8f4cf..b53bc92e 100644
--- a/pynets/__init__.py
+++ b/pynets/__init__.py
@@ -12,12 +12,7 @@
     __packagename__,
 )
 
-from .stats import *
-from .registration import *
-from .dmri import *
-from .fmri import *
-from .core import *
-from .plotting import *
+from . import *
 import warnings
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/_version.py b/pynets/_version.py
index f38d251e..2fd2815d 100644
--- a/pynets/_version.py
+++ b/pynets/_version.py
@@ -8,11 +8,11 @@
 
 version_json = '''
 {
- "date": "2020-07-01T01:48:40-0500",
- "dirty": true,
+ "date": "Wed 05 Jan 2022 02:59:17 PM CST",
+ "dirty": false,
  "error": null,
- "full-revisionid": "300ad2700c5f4464ffff136b768f5b0cb2602898",
- "version": "0+untagged.1301.g300ad27.dirty"
+ "full-revisionid": "060f878f90a09be8d1945c378d1df8c533d4e1de",
+ "version": "1.2"
 }
 '''  # END VERSION_JSON
 
diff --git a/pynets/runconfig.yaml b/pynets/advanced.yaml
similarity index 100%
rename from pynets/runconfig.yaml
rename to pynets/advanced.yaml
diff --git a/pynets/cli/__init__.py b/pynets/cli/__init__.py
index edd9b60a..0d98cf99 100644
--- a/pynets/cli/__init__.py
+++ b/pynets/cli/__init__.py
@@ -9,7 +9,7 @@
     __packagename__,
 )
 
-from ..stats import *
+from ..statistics import *
 from ..registration import *
 from ..dmri import *
 from ..fmri import *
diff --git a/pynets/cli/pynets_benchmark.py b/pynets/cli/pynets_benchmark.py
index e555bd87..4a04e560 100644
--- a/pynets/cli/pynets_benchmark.py
+++ b/pynets/cli/pynets_benchmark.py
@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 """
+Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@authors: Derek Pisner
 """
-from pynets.stats.benchmarking import *
+from pynets.statistics.group.benchmarking import *
 
 
 def main():
@@ -14,7 +14,7 @@ def main():
     from joblib import Parallel, delayed
     import tempfile
     import dill
-    from pynets.stats.utils import make_subject_dict, cleanNullTerms, \
+    from pynets.statistics.utils import make_subject_dict, cleanNullTerms, \
         get_ensembles_top, get_ensembles_embedding, \
         build_grid
     from colorama import Fore, Style
diff --git a/pynets/cli/pynets_bids.py b/pynets/cli/pynets_bids.py
index c53d10c9..274220bc 100644
--- a/pynets/cli/pynets_bids.py
+++ b/pynets/cli/pynets_bids.py
@@ -1,8 +1,8 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 """
+Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import bids
 
@@ -547,14 +547,14 @@ def main():
 
     if s3 or len(sec_s3_objs) > 0:
         from boto3.session import Session
-        from pynets.core import cloud_utils
+        from pynets.core import cloud
         from pynets.core.utils import as_directory
 
         home = os.path.expanduser("~")
-        creds = bool(cloud_utils.get_credentials())
+        creds = bool(cloud.get_credentials())
 
         if s3:
-            buck, remo = cloud_utils.parse_path(bids_args.bids_dir)
+            buck, remo = cloud.parse_path(bids_args.bids_dir)
             os.makedirs(f"{home}/.pynets", exist_ok=True)
             os.makedirs(f"{home}/.pynets/input", exist_ok=True)
             os.makedirs(f"{home}/.pynets/output", exist_ok=True)
@@ -576,31 +576,31 @@ def main():
                         info = "sub-" + partic + "/ses-" + ses
                     elif ses is None:
                         info = "sub-" + partic
-                    cloud_utils.s3_get_data(
+                    cloud.s3_get_data(
                         buck, remo, bids_dir, modality, info=info)
             elif analysis_level == "group":
                 if len(session_label) > 1 and session_label[0] != "None":
                     for ses in session_label:
                         info = "ses-" + ses
-                        cloud_utils.s3_get_data(
+                        cloud.s3_get_data(
                             buck, remo, bids_dir, modality, info=info
                         )
                 else:
-                    cloud_utils.s3_get_data(buck, remo, bids_dir, modality)
+                    cloud.s3_get_data(buck, remo, bids_dir, modality)
 
         if len(sec_s3_objs) > 0:
-            [access_key, secret_key] = cloud_utils.get_credentials()
+            [access_key, secret_key] = cloud.get_credentials()
 
             session = Session(
                 aws_access_key_id=access_key, aws_secret_access_key=secret_key
             )
 
             s3_r = session.resource("s3")
-            s3_c = cloud_utils.s3_client(service="s3")
+            s3_c = cloud.s3_client(service="s3")
             sec_dir = as_directory(
                 home + "/.pynets/secondary_files", remove=False)
             for s3_obj in [i for i in sec_s3_objs if i is not None]:
-                buck, remo = cloud_utils.parse_path(s3_obj)
+                buck, remo = cloud.parse_path(s3_obj)
                 s3_c.download_file(
                     buck, remo, f"{sec_dir}/{os.path.basename(s3_obj)}")
 
@@ -842,9 +842,9 @@ def main():
 
     if bids_args.push_location:
         print(f"Pushing to s3 at {bids_args.push_location}.")
-        push_buck, push_remo = cloud_utils.parse_path(bids_args.push_location)
+        push_buck, push_remo = cloud.parse_path(bids_args.push_location)
         for id in id_list:
-            cloud_utils.s3_push_data(
+            cloud.s3_push_data(
                 push_buck,
                 push_remo,
                 output_dir,
diff --git a/pynets/cli/pynets_cloud.py b/pynets/cli/pynets_cloud.py
index d90f3b5b..2730a6ae 100644
--- a/pynets/cli/pynets_cloud.py
+++ b/pynets/cli/pynets_cloud.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import subprocess
 import re
@@ -41,7 +40,41 @@ def batch_submit(
 ):
     """Searches through an S3 bucket, gets all subject-ids, creates json
     files for each, submits batch jobs, and returns list of job ids to query
-    status upon later."""
+    status upon later.
+
+    Parameters
+    __________
+    bucket : str
+        s3 bucket
+    dataset,
+    push_dir,
+    modality,
+    participant_label,
+    session_label,
+    parcellation,
+    cluster_mask,
+    roi : str
+        File path to binarized/boolean region-of-interest Nifti1Image file
+    ref : str
+        File path to reference Nifti1Image to use as the target for alignment.
+    way : str
+        File path to tractography constraint mask array in native diffusion
+        space.
+    plugin: plugin name or object
+        Plugin to use for execution. You can create your own plugins for
+        execution.
+    resources : str
+        Maximum number of cores and GB of memory, stated as two integers
+        seperated by comma. Otherwise, default is `auto`, which uses all
+        available resources detected on the compute node(s) used for
+        execution.
+    working_dir : str
+        Path to the working directory to perform SyN and save outputs
+    verbose : bool
+        Verbose print for debugging
+    jobdir : str
+        Directory of batch jobs to generate/check up on credentials
+    """
 
     print(f"Getting list from s3://{bucket}/{dataset}/...")
     seshs = crawl_bucket(bucket, dataset, jobdir)
@@ -78,6 +111,7 @@ def batch_submit(
 
 def crawl_bucket(bucket, path, jobdir):
     """Gets subject list for a given s3 bucket and path
+
     Parameters
     ----------
     bucket : str
@@ -86,6 +120,7 @@ def crawl_bucket(bucket, path, jobdir):
         The directory where the dataset is stored on the S3 bucket
     jobdir : str
         Directory of batch jobs to generate/check up on
+
     Returns
     -------
     OrderedDict
@@ -276,12 +311,14 @@ def create_json(
 
 def submit_jobs(jobs, jobdir):
     """Give list of jobs to submit, submits them to AWS Batch
+
     Parameters
     ----------
     jobs : list
         Name of the json files for all jobs to submit
     jobdir : str
         Directory of batch jobs to generate/check up on
+
     Returns
     -------
     int
@@ -307,6 +344,7 @@ def submit_jobs(jobs, jobdir):
 
 def kill_jobs(jobdir, reason='"Killing job"'):
     """Given a list of jobs, kills them all
+
     Parameters
     ----------
     jobdir : str
diff --git a/pynets/cli/pynets_config.py b/pynets/cli/pynets_config.py
index 83cbb4a3..572f2d9b 100644
--- a/pynets/cli/pynets_config.py
+++ b/pynets/cli/pynets_config.py
@@ -1,4 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue Nov  7 10:40:07 2017
+Copyright (C) 2017
+"""
 import sys
 import pkg_resources
 import yaml
@@ -8,7 +13,7 @@
 
 def load_runconfig():
     with open(pkg_resources.resource_filename("pynets",
-                                              "runconfig.yaml"),
+                                              "advanced.yaml"),
               mode='r+') as stream:
         advanced_params = yaml.load(stream, Loader=yaml.FullLoader)
     stream.close()
@@ -17,7 +22,7 @@ def load_runconfig():
 
 def save_runconfig(advanced_params):
     with open(pkg_resources.resource_filename("pynets",
-                                              "runconfig.yaml"),
+                                              "advanced.yaml"),
               mode='w+') as stream:
         yaml.dump(advanced_params, stream)
     stream.close()
diff --git a/pynets/cli/pynets_predict.py b/pynets/cli/pynets_predict.py
index 764237d9..c2110ade 100644
--- a/pynets/cli/pynets_predict.py
+++ b/pynets/cli/pynets_predict.py
@@ -1,10 +1,9 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 """
+Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@authors: Derek Pisner
 """
-from pynets.stats.prediction import *
 
 
 def get_parser():
@@ -195,10 +194,9 @@ def main():
     import os
     import sys
     import dill
-    from pynets.stats.utils import make_feature_space_dict, \
+    from pynets.statistics.utils import make_feature_space_dict, \
         make_subject_dict, cleanNullTerms
     from pynets.core.utils import mergedicts
-    from colorama import Fore, Style
     try:
         import pynets
     except ImportError:
@@ -257,7 +255,6 @@ def main():
     # ]
     mets = []
 
-    import sys
     print(f"parcellations = {parcellations}")
     print(f"sessions = {sessions}")
     print(f"base_dir = {base_dir}")
@@ -484,7 +481,7 @@ def main():
     import gc
     import json
     from multiprocessing import set_start_method, Process, Manager
-    from pynets.stats.prediction import build_predict_workflow
+    from pynets.statistics.group.prediction import build_predict_workflow
 
     try:
         set_start_method("forkserver")
diff --git a/pynets/cli/pynets_run.py b/pynets/cli/pynets_run.py
index fa7799ff..c97b7d7e 100755
--- a/pynets/cli/pynets_run.py
+++ b/pynets/cli/pynets_run.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 
@@ -111,7 +110,7 @@ def get_parser():
              "brain mask Nifti images in the case of running multiple "
              "participants, in which case paths should be separated by "
              "a space. If no brain mask is supplied, the template mask will "
-             "be used (see runconfig.yaml).\n",
+             "be used (see advanced.yaml).\n",
     )
     parser.add_argument(
         "-conf",
@@ -531,10 +530,10 @@ def get_parser():
         "-pm",
         metavar="Cores,memory",
         default="auto",
-        help="Number of cores to use, number of GB of memory to use for single"
-             " subject run, entered as two integers seperated by comma. "
-             "Otherwise, default is `auto`, which uses all resources "
-             "detected on the current compute node.\n",
+        help="Maximum number of cores and GB of memory, stated as two integers "
+             "seperated by comma. Otherwise, default is `auto`, which uses all "
+             "available resources detected on the compute node(s) used for "
+             "execution.\n",
     )
     parser.add_argument(
         "-plug",
@@ -565,6 +564,13 @@ def get_parser():
         action="store_true",
         help="Disable post-workflow clean-up of temporary runtime metadata.\n",
     )
+    parser.add_argument(
+        "-config",
+        metavar="Advanced configuration file",
+        default="advanced.yaml",
+        help="Optionally override advanced configuration parameters. "
+             "Default is advanced.yaml.\n",
+    )
     parser.add_argument(
         "-work",
         metavar="Working directory",
@@ -583,6 +589,7 @@ def build_workflow(args, retval):
     import os
     import glob
     import ast
+    import pkg_resources
     import os.path as op
     import timeit
     from datetime import timedelta
@@ -633,6 +640,11 @@ def build_workflow(args, retval):
     start_time = timeit.default_timer()
     print(Style.RESET_ALL)
 
+    adv_config = args.config
+    if adv_config != 'advanced.yaml':
+        os.replace(adv_config,
+                   pkg_resources.resource_filename("pynets", "advanced.yaml"))
+
     # Hard-coded:
     hardcoded_params = load_runconfig()
 
@@ -2282,7 +2294,8 @@ def build_workflow(args, retval):
 
     warnings.filterwarnings("ignore")
     from pynets.core.utils import collectpandasjoin
-    from pynets.core.interfaces import CombineOutputs, NetworkAnalysis
+    from pynets.core.interfaces import CombineOutputs
+    from pynets.statistics.interfaces import NetworkAnalysis
     from nipype.pipeline import engine as pe
     from nipype.interfaces import utility as niu
     from pynets.core.workflows import workflow_selector
diff --git a/pynets/core/__init__.py b/pynets/core/__init__.py
index 96091956..987e2591 100644
--- a/pynets/core/__init__.py
+++ b/pynets/core/__init__.py
@@ -3,7 +3,7 @@
 __author__ = """Derek Pisner"""
 __email__ = "dpisner@utexas.edu"
 
-from . import *
+from . import cloud, interfaces, nodemaker, thresholding, utils, workflows
 import warnings
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/core/cloud.py b/pynets/core/cloud.py
index a20e527a..25d7c745 100644
--- a/pynets/core/cloud.py
+++ b/pynets/core/cloud.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 from configparser import ConfigParser
 import os
diff --git a/pynets/core/interfaces.py b/pynets/core/interfaces.py
index 560c5354..b448fe85 100755
--- a/pynets/core/interfaces.py
+++ b/pynets/core/interfaces.py
@@ -3,7 +3,6 @@
 """
 Created on Fri Nov 10 15:44:46 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import numpy as np
@@ -12,13 +11,10 @@
     import indexed_gzip
 import nibabel as nib
 from nipype.interfaces.base import (
-    BaseInterface,
     BaseInterfaceInputSpec,
     TraitedSpec,
-    File,
     traits,
     SimpleInterface,
-    Directory,
 )
 
 warnings.filterwarnings("ignore")
@@ -58,8 +54,6 @@ class FetchNodesLabels(SimpleInterface):
     output_spec = _FetchNodesLabelsOutputSpec
 
     def _run_interface(self, runtime):
-        import sys
-        import pkg_resources
         from pynets.core import utils, nodemaker
         from nipype.utils.filemanip import fname_presuffix, copyfile
         from nilearn.image import concat_imgs
@@ -372,55 +366,6 @@ def _run_interface(self, runtime):
         return runtime
 
 
-class NetworkAnalysisInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for NetworkAnalysis"""
-
-    ID = traits.Any(mandatory=True)
-    subnet = traits.Any(mandatory=False)
-    thr = traits.Any(mandatory=True)
-    conn_model = traits.Str(mandatory=True)
-    est_path = File(exists=True, mandatory=True)
-    roi = traits.Any(mandatory=False)
-    prune = traits.Any(mandatory=False)
-    norm = traits.Any(mandatory=False)
-    binary = traits.Bool(False, usedefault=True)
-
-
-class NetworkAnalysisOutputSpec(TraitedSpec):
-    """Output interface wrapper for NetworkAnalysis"""
-
-    out_path_neat = File(exists=True, mandatory=True)
-
-
-class NetworkAnalysis(BaseInterface):
-    """Interface wrapper for NetworkAnalysis"""
-
-    input_spec = NetworkAnalysisInputSpec
-    output_spec = NetworkAnalysisOutputSpec
-
-    def _run_interface(self, runtime):
-        from pynets.stats.netstats import extractnetstats
-
-        out = extractnetstats(
-            self.inputs.ID,
-            self.inputs.subnet,
-            self.inputs.thr,
-            self.inputs.conn_model,
-            self.inputs.est_path,
-            self.inputs.roi,
-            self.inputs.prune,
-            self.inputs.norm,
-            self.inputs.binary,
-        )
-        setattr(self, "_outpath", out)
-        return runtime
-
-    def _list_outputs(self):
-        import os.path as op
-
-        return {"out_path_neat": op.abspath(getattr(self, "_outpath"))}
-
-
 class CombineOutputsInputSpec(BaseInterfaceInputSpec):
     """Input interface wrapper for CombineOutputs"""
 
@@ -462,2984 +407,3 @@ def _run_interface(self, runtime):
 
     def _list_outputs(self):
         return {"combination_complete": getattr(self, "_combination_complete")}
-
-
-class _IndividualClusteringInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for IndividualClustering"""
-
-    func_file = File(exists=True, mandatory=True)
-    conf = traits.Any(mandatory=False)
-    clust_mask = File(exists=True, mandatory=True)
-    ID = traits.Any(mandatory=True)
-    k = traits.Any(mandatory=True)
-    clust_type = traits.Str(mandatory=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    local_corr = traits.Str("allcorr", mandatory=True, usedefault=True)
-    mask = traits.Any(mandatory=False)
-    outdir = traits.Str(mandatory=True)
-    basedir_path = Directory(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    simple = traits.Bool(False, usedefault=True)
-
-
-class _IndividualClusteringOutputSpec(TraitedSpec):
-    """Output interface wrapper for IndividualClustering"""
-
-    parcellation = File(exists=True)
-    atlas = traits.Str(mandatory=True)
-    clustering = traits.Bool(True, usedefault=True)
-    clust_mask = File(exists=True, mandatory=True)
-    k = traits.Any(mandatory=True)
-    clust_type = traits.Str(mandatory=True)
-    func_file = File(exists=True, mandatory=True)
-
-
-class IndividualClustering(SimpleInterface):
-    """Interface wrapper for IndividualClustering"""
-
-    input_spec = _IndividualClusteringInputSpec
-    output_spec = _IndividualClusteringOutputSpec
-
-    def _run_interface(self, runtime):
-        import os
-        import gc
-        import time
-        import nibabel as nib
-        from pynets.core.utils import load_runconfig
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        from pynets.fmri import clustools
-        from pynets.registration.utils import orient_reslice
-        from joblib import Parallel, delayed
-        from joblib.externals.loky.backend import resource_tracker
-        from pynets.registration import utils as regutils
-        from pynets.core.utils import decompress_nifti
-        import pkg_resources
-        import shutil
-        import tempfile
-        resource_tracker.warnings = None
-
-        template = pkg_resources.resource_filename(
-            "pynets", f"templates/{self.inputs.template_name}_brain_"
-                      f"{self.inputs.vox_size}.nii.gz"
-        )
-
-        template_tmp_path = fname_presuffix(
-            template, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            template,
-            template_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        hardcoded_params = load_runconfig()
-        c_boot = hardcoded_params["c_boot"][0]
-        nthreads = hardcoded_params["omp_threads"][0]
-
-        clust_list = ["kmeans", "ward", "complete", "average", "ncut", "rena"]
-
-        clust_mask_temp_path = orient_reslice(
-            self.inputs.clust_mask, runtime.cwd, self.inputs.vox_size
-        )
-        cm_suf = os.path.basename(self.inputs.clust_mask).split('.nii')[0]
-        clust_mask_in_t1w_path = f"{runtime.cwd}/clust_mask-" \
-                                 f"{cm_suf}_in_t1w.nii.gz"
-
-        t1w_brain_tmp_path = fname_presuffix(
-            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w_brain,
-            t1w_brain_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        mni2t1w_warp_tmp_path = fname_presuffix(
-            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1w_warp,
-            mni2t1w_warp_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        mni2t1_xfm_tmp_path = fname_presuffix(
-            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1_xfm,
-            mni2t1_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        clust_mask_in_t1w = regutils.roi2t1w_align(
-            clust_mask_temp_path,
-            t1w_brain_tmp_path,
-            mni2t1_xfm_tmp_path,
-            mni2t1w_warp_tmp_path,
-            clust_mask_in_t1w_path,
-            template_tmp_path,
-            self.inputs.simple,
-        )
-        time.sleep(0.5)
-
-        if self.inputs.mask:
-            out_name_mask = fname_presuffix(
-                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mask,
-                out_name_mask,
-                copy=True,
-                use_hardlink=False)
-        else:
-            out_name_mask = None
-
-        out_name_func_file = fname_presuffix(
-            self.inputs.func_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.func_file,
-            out_name_func_file,
-            copy=True,
-            use_hardlink=False)
-        out_name_func_file = decompress_nifti(out_name_func_file)
-
-        if self.inputs.conf:
-            out_name_conf = fname_presuffix(
-                self.inputs.conf, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.conf,
-                out_name_conf,
-                copy=True,
-                use_hardlink=False)
-        else:
-            out_name_conf = None
-
-        nip = clustools.NiParcellate(
-            func_file=out_name_func_file,
-            clust_mask=clust_mask_in_t1w,
-            k=int(self.inputs.k),
-            clust_type=self.inputs.clust_type,
-            local_corr=self.inputs.local_corr,
-            outdir=self.inputs.outdir,
-            conf=out_name_conf,
-            mask=out_name_mask,
-        )
-
-        atlas = nip.create_clean_mask()
-        nip.create_local_clustering(overwrite=True, r_thresh=0.4)
-
-        if self.inputs.clust_type in clust_list:
-            if float(c_boot) > 1:
-                import random
-                from joblib import Memory
-                from joblib.externals.loky import get_reusable_executor
-                print(
-                    f"Performing circular block bootstrapping with {c_boot}"
-                    f" iterations..."
-                )
-                ts_data, block_size = nip.prep_boot()
-
-                cache_dir = tempfile.mkdtemp()
-                memory = Memory(cache_dir, verbose=0)
-                ts_data = memory.cache(ts_data)
-
-                def create_bs_imgs(ts_data, block_size, clust_mask_corr_img):
-                    import nibabel as nib
-                    from nilearn.masking import unmask
-                    from pynets.fmri.estimation import timeseries_bootstrap
-                    boot_series = timeseries_bootstrap(
-                        ts_data.func, block_size)[0].astype('float32')
-                    return unmask(boot_series, clust_mask_corr_img)
-
-                def run_bs_iteration(i, ts_data, work_dir, local_corr,
-                                     clust_type, _local_conn_mat_path,
-                                     num_conn_comps, _clust_mask_corr_img,
-                                     _standardize, _detrending, k,
-                                     _local_conn, conf, _dir_path,
-                                     _conn_comps):
-                    import os
-                    import time
-                    import gc
-                    from pynets.fmri.clustools import parcellate
-                    print(f"\nBootstrapped iteration: {i}")
-                    out_path = f"{work_dir}/boot_parc_tmp_{str(i)}.nii.gz"
-
-                    boot_img = create_bs_imgs(ts_data, block_size,
-                                              _clust_mask_corr_img)
-                    try:
-                        parcellation = parcellate(boot_img, local_corr,
-                                                  clust_type,
-                                                  _local_conn_mat_path,
-                                                  num_conn_comps,
-                                                  _clust_mask_corr_img,
-                                                  _standardize,
-                                                  _detrending, k, _local_conn,
-                                                  conf, _dir_path,
-                                                  _conn_comps)
-                        parcellation.to_filename(out_path)
-                        parcellation.uncache()
-                        boot_img.uncache()
-                        gc.collect()
-                    except BaseException:
-                        boot_img.uncache()
-                        gc.collect()
-                        return None
-                    _clust_mask_corr_img.uncache()
-                    return out_path
-
-                time.sleep(random.randint(1, 5))
-                counter = 0
-                boot_parcellations = []
-                while float(counter) < float(c_boot):
-                    with Parallel(n_jobs=nthreads, max_nbytes='8000M',
-                                  backend='loky', mmap_mode='r+',
-                                  temp_folder=cache_dir,
-                                  verbose=10) as parallel:
-                        iter_bootedparcels = parallel(
-                            delayed(run_bs_iteration)(
-                                i, ts_data, runtime.cwd, nip.local_corr,
-                                nip.clust_type, nip._local_conn_mat_path,
-                                nip.num_conn_comps, nip._clust_mask_corr_img,
-                                nip._standardize, nip._detrending, nip.k,
-                                nip._local_conn, nip.conf, nip._dir_path,
-                                nip._conn_comps) for i in
-                            range(c_boot))
-
-                        boot_parcellations.extend([i for i in
-                                                   iter_bootedparcels if
-                                                   i is not None])
-                        counter = len(boot_parcellations)
-                        del iter_bootedparcels
-                        gc.collect()
-
-                print('Bootstrapped samples complete:')
-                print(boot_parcellations)
-                print("Creating spatially-constrained consensus "
-                      "parcellation...")
-                consensus_parcellation = clustools.ensemble_parcellate(
-                    boot_parcellations,
-                    int(self.inputs.k)
-                )
-                nib.save(consensus_parcellation, nip.parcellation)
-                memory.clear(warn=False)
-                shutil.rmtree(cache_dir, ignore_errors=True)
-                del parallel, memory, cache_dir
-                get_reusable_executor().shutdown(wait=True)
-                gc.collect()
-
-                for i in boot_parcellations:
-                    if i is not None:
-                        if os.path.isfile(i):
-                            os.system(f"rm -f {i} &")
-            else:
-                print(
-                    "Creating spatially-constrained parcellation...")
-                out_path = f"{runtime.cwd}/{atlas}_{str(self.inputs.k)}.nii.gz"
-                func_img = nib.load(out_name_func_file)
-                parcellation = clustools.parcellate(func_img,
-                                                    self.inputs.local_corr,
-                                                    self.inputs.clust_type,
-                                                    nip._local_conn_mat_path,
-                                                    nip.num_conn_comps,
-                                                    nip._clust_mask_corr_img,
-                                                    nip._standardize,
-                                                    nip._detrending, nip.k,
-                                                    nip._local_conn,
-                                                    nip.conf, nip._dir_path,
-                                                    nip._conn_comps)
-                parcellation.to_filename(out_path)
-
-        else:
-            raise ValueError(
-                "Clustering method not recognized. See: "
-                "https://nilearn.github.io/modules/generated/"
-                "nilearn.regions.Parcellations."
-                "html#nilearn.regions.Parcellations")
-
-        # Give it a minute
-        ix = 0
-        while not os.path.isfile(nip.parcellation) and ix < 60:
-            print('Waiting for clustered parcellation...')
-            time.sleep(1)
-            ix += 1
-
-        if not os.path.isfile(nip.parcellation):
-            raise FileNotFoundError(f"Parcellation clustering failed for"
-                                    f" {nip.parcellation}")
-
-        self._results["atlas"] = atlas
-        self._results["parcellation"] = nip.parcellation
-        self._results["clust_mask"] = clust_mask_in_t1w_path
-        self._results["k"] = self.inputs.k
-        self._results["clust_type"] = self.inputs.clust_type
-        self._results["clustering"] = True
-        self._results["func_file"] = self.inputs.func_file
-
-        reg_tmp = [
-            t1w_brain_tmp_path,
-            mni2t1w_warp_tmp_path,
-            mni2t1_xfm_tmp_path,
-            template_tmp_path,
-            out_name_func_file
-        ]
-        for j in reg_tmp:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        gc.collect()
-
-        return runtime
-
-
-class _ExtractTimeseriesInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for ExtractTimeseries"""
-
-    conf = traits.Any(mandatory=False)
-    func_file = File(exists=True, mandatory=True)
-    coords = traits.Any(mandatory=True)
-    roi = traits.Any(mandatory=False)
-    dir_path = Directory(exists=True, mandatory=True)
-    ID = traits.Any(mandatory=True)
-    subnet = traits.Any(mandatory=False)
-    smooth = traits.Any(mandatory=True)
-    atlas = traits.Any(mandatory=False)
-    parcellation = traits.Any(mandatory=False)
-    labels = traits.Any(mandatory=True)
-    hpass = traits.Any(mandatory=True)
-    mask = traits.Any(mandatory=False)
-    parc = traits.Bool()
-    node_radius = traits.Any(mandatory=False)
-    net_parcels_nii_path = traits.Any(mandatory=False)
-    extract_strategy = traits.Str("mean", mandatory=False, usedefault=True)
-
-
-class _ExtractTimeseriesOutputSpec(TraitedSpec):
-    """Output interface wrapper for ExtractTimeseries"""
-
-    ts_within_nodes = traits.Any(mandatory=True)
-    node_radius = traits.Any(mandatory=True)
-    smooth = traits.Any(mandatory=True)
-    dir_path = Directory(exists=True, mandatory=True)
-    atlas = traits.Any(mandatory=False)
-    parcellation = traits.Any(mandatory=False)
-    labels = traits.Any(mandatory=True)
-    coords = traits.Any(mandatory=True)
-    hpass = traits.Any(mandatory=True)
-    roi = traits.Any(mandatory=True)
-    extract_strategy = traits.Any(mandatory=False)
-
-
-class ExtractTimeseries(SimpleInterface):
-    """Interface wrapper for ExtractTimeseries"""
-
-    input_spec = _ExtractTimeseriesInputSpec
-    output_spec = _ExtractTimeseriesOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        from pynets.fmri import estimation
-        from pynets.core.utils import decompress_nifti
-
-        # Decompressing each image to facilitate more seamless memory mapping
-        if self.inputs.net_parcels_nii_path:
-            out_name_net_parcels_nii_path = fname_presuffix(
-                self.inputs.net_parcels_nii_path, suffix="_tmp",
-                newpath=runtime.cwd)
-            copyfile(
-                self.inputs.net_parcels_nii_path,
-                out_name_net_parcels_nii_path,
-                copy=True,
-                use_hardlink=False,
-            )
-            out_name_net_parcels_nii_path = decompress_nifti(
-                out_name_net_parcels_nii_path)
-        else:
-            out_name_net_parcels_nii_path = None
-
-        if self.inputs.mask:
-            out_name_mask = fname_presuffix(
-                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mask,
-                out_name_mask,
-                copy=True,
-                use_hardlink=False)
-            out_name_mask = decompress_nifti(out_name_mask)
-        else:
-            out_name_mask = None
-
-        out_name_func_file = fname_presuffix(
-            self.inputs.func_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.func_file,
-            out_name_func_file,
-            copy=True,
-            use_hardlink=False)
-        out_name_func_file = decompress_nifti(out_name_func_file)
-
-        if self.inputs.conf:
-            out_name_conf = fname_presuffix(
-                self.inputs.conf, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.conf,
-                out_name_conf,
-                copy=True,
-                use_hardlink=False)
-        else:
-            out_name_conf = None
-
-        te = estimation.TimeseriesExtraction(
-            net_parcels_nii_path=out_name_net_parcels_nii_path,
-            node_radius=self.inputs.node_radius,
-            conf=out_name_conf,
-            func_file=out_name_func_file,
-            roi=self.inputs.roi,
-            dir_path=self.inputs.dir_path,
-            ID=self.inputs.ID,
-            subnet=self.inputs.subnet,
-            smooth=self.inputs.smooth,
-            hpass=self.inputs.hpass,
-            mask=out_name_mask,
-            extract_strategy=self.inputs.extract_strategy,
-        )
-
-        te.prepare_inputs()
-
-        te.extract_ts_parc()
-
-        te.save_and_cleanup()
-
-        try:
-            assert (
-                len(self.inputs.coords)
-                == len(self.inputs.labels)
-                == te.ts_within_nodes.shape[1]
-            )
-        except AssertionError as e:
-            e.args += ('Coords: ', len(self.inputs.coords),
-                       self.inputs.coords, 'Labels:',
-                       len(self.inputs.labels),
-                       self.inputs.labels, te.ts_within_nodes.shape)
-
-        self._results["ts_within_nodes"] = te.ts_within_nodes
-        self._results["node_radius"] = te.node_radius
-        self._results["smooth"] = te.smooth
-        self._results["extract_strategy"] = te.extract_strategy
-        self._results["dir_path"] = te.dir_path
-        self._results["atlas"] = self.inputs.atlas
-        self._results["parcellation"] = self.inputs.parcellation
-        self._results["labels"] = self.inputs.labels
-        self._results["coords"] = self.inputs.coords
-        self._results["hpass"] = te.hpass
-        self._results["roi"] = self.inputs.roi
-
-        del te
-        gc.collect()
-
-        return runtime
-
-
-class _PlotStructInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for PlotStruct"""
-
-    conn_matrix = traits.Any()
-    conn_model = traits.Str(mandatory=True)
-    atlas = traits.Any(mandatory=False)
-    dir_path = Directory(exists=True, mandatory=True)
-    ID = traits.Any(mandatory=True)
-    subnet = traits.Any(mandatory=True)
-    labels = traits.Array(mandatory=True)
-    roi = traits.Any(mandatory=True)
-    coords = traits.Array(mandatory=True)
-    thr = traits.Any(mandatory=True)
-    node_radius = traits.Any(mandatory=True)
-    edge_threshold = traits.Any(mandatory=True)
-    prune = traits.Any(mandatory=True)
-    parcellation = traits.Any(mandatory=False)
-    target_samples = traits.Any(mandatory=True)
-    norm = traits.Any(mandatory=True)
-    binary = traits.Bool(mandatory=True)
-    track_type = traits.Any(mandatory=True)
-    directget = traits.Any(mandatory=True)
-    min_length = traits.Any(mandatory=True)
-    error_margin = traits.Any(mandatory=True)
-
-
-class _PlotStructOutputSpec(BaseInterfaceInputSpec):
-    """Output interface wrapper for PlotStruct"""
-
-    out = traits.Str
-
-
-class PlotStruct(SimpleInterface):
-    """Interface wrapper for PlotStruct"""
-
-    input_spec = _PlotStructInputSpec
-    output_spec = _PlotStructOutputSpec
-
-    def _run_interface(self, runtime):
-        from pynets.plotting import plot_gen
-
-        if isinstance(self.inputs.conn_matrix, str):
-            self.inputs.conn_matrix = np.load(self.inputs.conn_matrix)
-
-        assert (
-            len(self.inputs.coords)
-            == len(self.inputs.labels)
-            == self.inputs.conn_matrix.shape[0]
-        )
-        if self.inputs.coords.ndim == 1:
-            print("Only 1 node detected. Plotting is not applicable...")
-        else:
-            plot_gen.plot_all_struct(
-                self.inputs.conn_matrix,
-                self.inputs.conn_model,
-                self.inputs.atlas,
-                self.inputs.dir_path,
-                self.inputs.ID,
-                self.inputs.subnet,
-                self.inputs.labels.tolist(),
-                self.inputs.roi,
-                [tuple(coord) for coord in self.inputs.coords.tolist()],
-                self.inputs.thr,
-                self.inputs.node_radius,
-                self.inputs.edge_threshold,
-                self.inputs.prune,
-                self.inputs.parcellation,
-                self.inputs.target_samples,
-                self.inputs.norm,
-                self.inputs.binary,
-                self.inputs.track_type,
-                self.inputs.directget,
-                self.inputs.min_length,
-                self.inputs.error_margin
-            )
-
-        self._results["out"] = "None"
-
-        return runtime
-
-
-class _PlotFuncInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for PlotFunc"""
-
-    conn_matrix = traits.Any()
-    conn_model = traits.Str(mandatory=True)
-    atlas = traits.Any(mandatory=False)
-    dir_path = Directory(exists=True, mandatory=True)
-    ID = traits.Any(mandatory=True)
-    subnet = traits.Any(mandatory=True)
-    labels = traits.Array(mandatory=True)
-    roi = traits.Any(mandatory=True)
-    coords = traits.Array(mandatory=True)
-    thr = traits.Any(mandatory=True)
-    node_radius = traits.Any(mandatory=True)
-    edge_threshold = traits.Any(mandatory=True)
-    smooth = traits.Any(mandatory=True)
-    prune = traits.Any(mandatory=True)
-    parcellation = traits.Any(mandatory=False)
-    norm = traits.Any(mandatory=True)
-    binary = traits.Bool(mandatory=True)
-    hpass = traits.Any(mandatory=True)
-    extract_strategy = traits.Any(mandatory=True)
-    edge_color_override = traits.Bool(mandatory=True)
-
-
-class _PlotFuncOutputSpec(BaseInterfaceInputSpec):
-    """Output interface wrapper for PlotFunc"""
-
-    out = traits.Str
-
-
-class PlotFunc(SimpleInterface):
-    """Interface wrapper for PlotFunc"""
-
-    input_spec = _PlotFuncInputSpec
-    output_spec = _PlotFuncOutputSpec
-
-    def _run_interface(self, runtime):
-        from pynets.plotting import plot_gen
-
-        if isinstance(self.inputs.conn_matrix, str):
-            self.inputs.conn_matrix = np.load(self.inputs.conn_matrix)
-
-        assert (
-            len(self.inputs.coords)
-            == len(self.inputs.labels)
-            == self.inputs.conn_matrix.shape[0]
-        )
-
-        if self.inputs.coords.ndim == 1:
-            print("Only 1 node detected. Plotting not applicable...")
-        else:
-            plot_gen.plot_all_func(
-                self.inputs.conn_matrix,
-                self.inputs.conn_model,
-                self.inputs.atlas,
-                self.inputs.dir_path,
-                self.inputs.ID,
-                self.inputs.subnet,
-                self.inputs.labels.tolist(),
-                self.inputs.roi,
-                [tuple(coord) for coord in self.inputs.coords.tolist()],
-                self.inputs.thr,
-                self.inputs.node_radius,
-                self.inputs.edge_threshold,
-                self.inputs.smooth,
-                self.inputs.prune,
-                self.inputs.parcellation,
-                self.inputs.norm,
-                self.inputs.binary,
-                self.inputs.hpass,
-                self.inputs.extract_strategy,
-                self.inputs.edge_color_override,
-            )
-
-        self._results["out"] = "None"
-
-        return runtime
-
-
-class _RegisterDWIInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterDWI"""
-
-    fa_path = File(exists=True, mandatory=True)
-    ap_path = File(exists=True, mandatory=True)
-    B0_mask = File(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    gtab_file = File(exists=True, mandatory=True)
-    dwi_file = File(exists=True, mandatory=True)
-    in_dir = traits.Any()
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    mask = traits.Any(mandatory=False)
-    force_create_mask = traits.Bool(True, usedefault=True)
-    simple = traits.Bool(False, usedefault=True)
-    overwrite = traits.Bool(False, usedefault=True)
-
-
-class _RegisterDWIOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterDWI"""
-
-    wm_in_dwi = File(exists=True, mandatory=True)
-    gm_in_dwi = File(exists=True, mandatory=True)
-    vent_csf_in_dwi = File(exists=True, mandatory=True)
-    csf_mask_dwi = File(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    B0_mask = File(exists=True, mandatory=True)
-    ap_path = File(exists=True, mandatory=True)
-    gtab_file = File(exists=True, mandatory=True)
-    dwi_file = File(exists=True, mandatory=True)
-    basedir_path = Directory(exists=True, mandatory=True)
-    t1w2dwi = File(exists=True, mandatory=True)
-    t1w_brain_mask_in_dwi = traits.Any(mandatory=False)
-    t1_aligned_mni = traits.Any(mandatory=False)
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    t1wtissue2dwi_xfm = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    t1w_brain_mask = File(exists=True, mandatory=True)
-    t1w2dwi_bbr_xfm = File(exists=True, mandatory=True)
-    t1w2dwi_xfm = File(exists=True, mandatory=True)
-    wm_gm_int_in_dwi = File(exists=True, mandatory=True)
-
-
-class RegisterDWI(SimpleInterface):
-    """Interface wrapper for RegisterDWI to create T1w->MNI->DWI mappings."""
-
-    input_spec = _RegisterDWIInputSpec
-    output_spec = _RegisterDWIOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import time
-        import glob
-        import os
-        import os.path as op
-        from pynets.registration import register
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        from pynets.registration.utils import orient_reslice
-
-        fa_tmp_path = fname_presuffix(
-            self.inputs.fa_path, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.fa_path,
-            fa_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        ap_tmp_path = fname_presuffix(
-            self.inputs.ap_path, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.ap_path,
-            ap_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        B0_mask_tmp_path = fname_presuffix(
-            self.inputs.B0_mask, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.B0_mask,
-            B0_mask_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        anat_mask_existing = [
-            i
-            for i in glob.glob(f"{self.inputs.in_dir}"
-                               f"/*_desc-brain_mask.nii.gz")
-            if "MNI" not in i
-        ]
-
-        # Copy T1w mask, if provided, else use existing, if detected, else
-        # compute a fresh one
-        if self.inputs.mask:
-            mask_tmp_path = fname_presuffix(
-                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mask,
-                mask_tmp_path,
-                copy=True,
-                use_hardlink=False)
-        else:
-            if len(anat_mask_existing) > 0 and \
-                    self.inputs.mask is None and \
-                    op.isfile(anat_mask_existing[0]) and \
-                    self.inputs.force_create_mask is False:
-                mask_tmp_path = fname_presuffix(
-                    anat_mask_existing[0], suffix="_tmp", newpath=runtime.cwd
-                )
-                copyfile(
-                    anat_mask_existing[0],
-                    mask_tmp_path,
-                    copy=True,
-                    use_hardlink=False)
-                mask_tmp_path = orient_reslice(
-                    mask_tmp_path, runtime.cwd, self.inputs.vox_size
-                )
-            else:
-                mask_tmp_path = None
-
-        gm_mask_existing = glob.glob(
-            self.inputs.in_dir +
-            "/*_label-GM_probseg.nii.gz")
-        if len(gm_mask_existing) > 0:
-            gm_mask = fname_presuffix(gm_mask_existing[0], newpath=runtime.cwd)
-            copyfile(
-                gm_mask_existing[0],
-                gm_mask,
-                copy=True,
-                use_hardlink=False)
-            gm_mask = orient_reslice(
-                gm_mask, runtime.cwd, self.inputs.vox_size
-            )
-        else:
-            gm_mask = None
-
-        wm_mask_existing = glob.glob(
-            self.inputs.in_dir +
-            "/*_label-WM_probseg.nii.gz")
-        if len(wm_mask_existing) > 0:
-            wm_mask = fname_presuffix(wm_mask_existing[0], newpath=runtime.cwd)
-            copyfile(
-                wm_mask_existing[0],
-                wm_mask,
-                copy=True,
-                use_hardlink=False)
-            wm_mask = orient_reslice(
-                wm_mask, runtime.cwd, self.inputs.vox_size
-            )
-        else:
-            wm_mask = None
-
-        csf_mask_existing = glob.glob(
-            self.inputs.in_dir + "/*_label-CSF_probseg.nii.gz"
-        )
-        if len(csf_mask_existing) > 0:
-            csf_mask = fname_presuffix(
-                csf_mask_existing[0], newpath=runtime.cwd)
-            copyfile(
-                csf_mask_existing[0],
-                csf_mask,
-                copy=True,
-                use_hardlink=False)
-            csf_mask = orient_reslice(
-                csf_mask, runtime.cwd, self.inputs.vox_size
-            )
-        else:
-            csf_mask = None
-
-        anat_file_tmp_path = fname_presuffix(
-            self.inputs.anat_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.anat_file,
-            anat_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        reg = register.DmriReg(
-            basedir_path=runtime.cwd,
-            fa_path=fa_tmp_path,
-            ap_path=ap_tmp_path,
-            B0_mask=B0_mask_tmp_path,
-            anat_file=anat_file_tmp_path,
-            vox_size=self.inputs.vox_size,
-            template_name=self.inputs.template_name,
-            simple=self.inputs.simple,
-        )
-
-        # Generate T1w brain mask
-        reg.gen_mask(mask_tmp_path)
-
-        # Perform anatomical segmentation
-        reg.gen_tissue(wm_mask, gm_mask, csf_mask, self.inputs.overwrite)
-
-        # Align t1w to mni template
-        # from joblib import Memory
-        # import os
-        # location = f"{outdir}/joblib_" \
-        #            f"{self.inputs.anat_file.split('.nii')[0]}"
-        # os.makedirs(location, exist_ok=True)
-        # memory = Memory(location)
-        # t1w2mni_align = memory.cache(reg.t1w2mni_align)
-        # t1w2mni_align()
-        reg.t1w2mni_align()
-        time.sleep(0.5)
-
-        if (self.inputs.overwrite is True) or (
-                op.isfile(reg.t1w2dwi) is False):
-            # Align t1w to dwi
-            reg.t1w2dwi_align()
-            time.sleep(0.5)
-
-        if (self.inputs.overwrite is True) or (
-            op.isfile(reg.wm_gm_int_in_dwi) is False
-        ):
-            # Align tissue
-            reg.tissue2dwi_align()
-            time.sleep(0.5)
-
-        self._results["wm_in_dwi"] = reg.wm_in_dwi
-        self._results["gm_in_dwi"] = reg.gm_in_dwi
-        self._results["vent_csf_in_dwi"] = reg.vent_csf_in_dwi
-        self._results["csf_mask_dwi"] = reg.csf_mask_dwi
-        self._results["anat_file"] = anat_file_tmp_path
-        self._results["t1w2dwi"] = reg.t1w2dwi
-        self._results["B0_mask"] = B0_mask_tmp_path
-        self._results["ap_path"] = ap_tmp_path
-        self._results["gtab_file"] = self.inputs.gtab_file
-        self._results["dwi_file"] = self.inputs.dwi_file
-        self._results["basedir_path"] = runtime.cwd
-        self._results["t1w_brain_mask_in_dwi"] = reg.t1w_brain_mask_in_dwi
-        self._results["t1_aligned_mni"] = reg.t1_aligned_mni
-        self._results["t1w_brain"] = reg.t1w_brain
-        self._results["mni2t1w_warp"] = reg.mni2t1w_warp
-        self._results["t1wtissue2dwi_xfm"] = reg.t1wtissue2dwi_xfm
-        self._results["mni2t1_xfm"] = reg.mni2t1_xfm
-        self._results["t1w_brain_mask"] = reg.t1w_brain_mask
-        self._results["t1w2dwi_bbr_xfm"] = reg.t1w2dwi_bbr_xfm
-        self._results["t1w2dwi_xfm"] = reg.t1w2dwi_xfm
-        self._results["wm_gm_int_in_dwi"] = reg.wm_gm_int_in_dwi
-
-        reg_tmp = [
-            fa_tmp_path,
-            mask_tmp_path,
-            reg.warp_t1w2mni,
-            reg.t1w_head,
-            reg.wm_edge,
-            reg.vent_mask_dwi,
-            reg.vent_mask_t1w,
-            reg.corpuscallosum_mask_t1w,
-            reg.corpuscallosum_dwi
-        ]
-        for j in reg_tmp:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        gc.collect()
-
-        return runtime
-
-
-class _RegisterAtlasDWIInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterAtlasDWI"""
-
-    atlas = traits.Any(mandatory=False)
-    subnet = traits.Any(mandatory=True)
-    parcellation4d = traits.Any(mandatory=True)
-    parcellation = traits.Any(mandatory=False)
-    basedir_path = Directory(exists=True, mandatory=True)
-    node_radius = traits.Any(mandatory=True)
-    fa_path = File(exists=True, mandatory=True)
-    ap_path = File(exists=True, mandatory=True)
-    B0_mask = File(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    coords = traits.Any(mandatory=True)
-    labels = traits.Any(mandatory=True)
-    gm_in_dwi = File(exists=True, mandatory=True)
-    vent_csf_in_dwi = File(exists=True, mandatory=True)
-    wm_in_dwi = File(exists=True, mandatory=True)
-    gtab_file = File(exists=True, mandatory=True)
-    dwi_file = File(exists=True, mandatory=True)
-    mask = traits.Any(mandatory=False)
-    waymask = traits.Any(mandatory=False)
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    t1wtissue2dwi_xfm = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    t1w_brain_mask = File(exists=True, mandatory=True)
-    t1_aligned_mni = File(exists=True, mandatory=True)
-    t1w2dwi_bbr_xfm = File(exists=True, mandatory=True)
-    t1w2dwi_xfm = File(exists=True, mandatory=True)
-    wm_gm_int_in_dwi = File(exists=True, mandatory=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    simple = traits.Bool(False, usedefault=True)
-
-
-class _RegisterAtlasDWIOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterAtlasDWI"""
-
-    dwi_aligned_atlas_wmgm_int = File(exists=True, mandatory=True)
-    dwi_aligned_atlas = File(exists=True, mandatory=True)
-    aligned_atlas_t1w = File(exists=True, mandatory=True)
-    node_radius = traits.Any(mandatory=True)
-    atlas = traits.Any(mandatory=False)
-    parcellation4d = traits.Any(mandatory=False)
-    parcellation = traits.Any(mandatory=False)
-    coords = traits.Any(mandatory=True)
-    labels = traits.Any(mandatory=True)
-    wm_in_dwi = File(exists=True, mandatory=True)
-    gm_in_dwi = File(exists=True, mandatory=True)
-    vent_csf_in_dwi = File(exists=True, mandatory=True)
-    B0_mask = File(exists=True, mandatory=True)
-    ap_path = File(exists=True, mandatory=True)
-    gtab_file = File(exists=True, mandatory=True)
-    dwi_file = File(exists=True, mandatory=True)
-    waymask_in_dwi = traits.Any(mandatory=False)
-
-
-class RegisterAtlasDWI(SimpleInterface):
-    """Interface wrapper for RegisterAtlasDWI."""
-
-    input_spec = _RegisterAtlasDWIInputSpec
-    output_spec = _RegisterAtlasDWIOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import time
-        import os
-        import os.path as op
-        from pynets.registration import utils as regutils
-        from pynets.core.nodemaker import \
-            drop_coords_labels_from_restricted_parcellation
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        import pkg_resources
-        from pynets.core import utils
-
-        template = pkg_resources.resource_filename(
-            "pynets", f"templates/{self.inputs.template_name}_brain_"
-                      f"{self.inputs.vox_size}.nii.gz"
-        )
-
-        template_tmp_path = fname_presuffix(
-            template, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            template,
-            template_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        if self.inputs.parcellation is None:
-            parcellation_tmp_path = None
-        else:
-            parcellation_tmp_path = fname_presuffix(
-                self.inputs.parcellation, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.parcellation,
-                parcellation_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-        if self.inputs.parcellation4d is None:
-            parcellation4d_tmp_path = None
-        else:
-            parcellation4d_tmp_path = fname_presuffix(
-                self.inputs.parcellation4d, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.parcellation4d,
-                parcellation4d_tmp_path,
-                copy=True,
-                use_hardlink=False,
-            )
-
-        fa_tmp_path = fname_presuffix(
-            self.inputs.fa_path, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.fa_path,
-            fa_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        ap_tmp_path = fname_presuffix(
-            self.inputs.ap_path, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.ap_path,
-            ap_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        B0_mask_tmp_path = fname_presuffix(
-            self.inputs.B0_mask, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.B0_mask,
-            B0_mask_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        t1w_brain_tmp_path = fname_presuffix(
-            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w_brain,
-            t1w_brain_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        mni2t1w_warp_tmp_path = fname_presuffix(
-            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1w_warp,
-            mni2t1w_warp_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        t1wtissue2dwi_xfm_tmp_path = fname_presuffix(
-            self.inputs.t1wtissue2dwi_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1wtissue2dwi_xfm,
-            t1wtissue2dwi_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        mni2t1_xfm_tmp_path = fname_presuffix(
-            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1_xfm,
-            mni2t1_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        t1w_brain_mask_tmp_path = fname_presuffix(
-            self.inputs.t1w_brain_mask, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w_brain_mask,
-            t1w_brain_mask_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        t1_aligned_mni_tmp_path = fname_presuffix(
-            self.inputs.t1_aligned_mni, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1_aligned_mni,
-            t1_aligned_mni_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        t1w2dwi_bbr_xfm_tmp_path = fname_presuffix(
-            self.inputs.t1w2dwi_bbr_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w2dwi_bbr_xfm,
-            t1w2dwi_bbr_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        t1w2dwi_xfm_tmp_path = fname_presuffix(
-            self.inputs.t1w2dwi_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w2dwi_xfm,
-            t1w2dwi_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        wm_gm_int_in_dwi_tmp_path = fname_presuffix(
-            self.inputs.wm_gm_int_in_dwi, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.wm_gm_int_in_dwi,
-            wm_gm_int_in_dwi_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        if self.inputs.subnet or self.inputs.waymask:
-            if self.inputs.waymask:
-                wm_suf = os.path.basename(self.inputs.waymask).split('.nii')[0]
-                atlas_name = f"{self.inputs.atlas}_" \
-                             f"{wm_suf}"
-            else:
-                atlas_name = f"{self.inputs.atlas}_{self.inputs.subnet}"
-        else:
-            atlas_name = f"{self.inputs.atlas}"
-
-        base_dir_tmp = f"{runtime.cwd}/atlas_{atlas_name}"
-        os.makedirs(base_dir_tmp, exist_ok=True)
-
-        mni2dwi_xfm = f"{base_dir_tmp}{'/'}{atlas_name}" \
-            f"{'_mni2dwi_xfm.mat'}"
-
-        aligned_atlas_t1mni = f"{base_dir_tmp}{'/'}{atlas_name}" \
-                              f"{'_t1w_mni.nii.gz'}"
-        aligned_atlas_skull = f"{base_dir_tmp}{'/'}{atlas_name}" \
-                              f"{'_t1w_skull.nii.gz'}"
-        dwi_aligned_atlas = f"{base_dir_tmp}{'/'}{atlas_name}" \
-                            f"{'_dwi_track.nii.gz'}"
-        dwi_aligned_atlas_wmgm_int = (
-            f"{base_dir_tmp}{'/'}{atlas_name}{'_dwi_track_wmgm_int.nii.gz'}"
-        )
-
-        if self.inputs.node_radius is not None:
-            atlas_name = f"{atlas_name}{'_'}{self.inputs.node_radius}"
-
-        # Apply warps/coregister atlas to dwi
-        [
-            dwi_aligned_atlas_wmgm_int,
-            dwi_aligned_atlas,
-            aligned_atlas_t1w,
-        ] = regutils.atlas2t1w2dwi_align(
-            parcellation_tmp_path,
-            parcellation4d_tmp_path,
-            atlas_name,
-            t1w_brain_tmp_path,
-            t1w_brain_mask_tmp_path,
-            mni2t1w_warp_tmp_path,
-            t1_aligned_mni_tmp_path,
-            ap_tmp_path,
-            mni2t1_xfm_tmp_path,
-            t1wtissue2dwi_xfm_tmp_path,
-            wm_gm_int_in_dwi_tmp_path,
-            aligned_atlas_t1mni,
-            aligned_atlas_skull,
-            dwi_aligned_atlas,
-            dwi_aligned_atlas_wmgm_int,
-            B0_mask_tmp_path,
-            mni2dwi_xfm,
-            self.inputs.simple,
-        )
-
-        # Correct coords and labels
-        [dwi_aligned_atlas, coords, labels] = \
-            drop_coords_labels_from_restricted_parcellation(
-            dwi_aligned_atlas, self.inputs.coords, self.inputs.labels)
-
-        if self.inputs.waymask:
-            waymask_tmp_path = fname_presuffix(
-                self.inputs.waymask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.waymask,
-                waymask_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            # Align waymask
-            waymask_in_t1w = (
-                f"{runtime.cwd}/waymask-"
-                f"{os.path.basename(self.inputs.waymask).split('.nii')[0]}_"
-                f"in_t1w.nii.gz")
-            waymask_in_dwi = (
-                f"{runtime.cwd}/waymask-"
-                f"{os.path.basename(self.inputs.waymask).split('.nii')[0]}_"
-                f"in_dwi.nii.gz")
-
-            waymask_in_dwi = regutils.waymask2dwi_align(
-                waymask_tmp_path,
-                t1w_brain_tmp_path,
-                ap_tmp_path,
-                mni2t1w_warp_tmp_path,
-                mni2t1_xfm_tmp_path,
-                t1wtissue2dwi_xfm_tmp_path,
-                waymask_in_t1w,
-                waymask_in_dwi,
-                B0_mask_tmp_path,
-                template_tmp_path,
-                self.inputs.simple,
-            )
-            time.sleep(0.5)
-            os.system(f"rm -f {waymask_tmp_path} &")
-        else:
-            waymask_in_dwi = None
-
-        if self.inputs.parcellation is None:
-            parcellation_out = self.inputs.parcellation4d
-            copyfile(
-                dwi_aligned_atlas,
-                f"{os.path.dirname(parcellation_out)}/"
-                f"{os.path.basename(dwi_aligned_atlas)}",
-                copy=True,
-                use_hardlink=False,
-            )
-        else:
-            parcellation_out = self.inputs.parcellation
-            copyfile(
-                dwi_aligned_atlas,
-                f"{os.path.dirname(parcellation_out)}/parcellations/"
-                f"{os.path.basename(dwi_aligned_atlas)}",
-                copy=True,
-                use_hardlink=False,
-            )
-
-        # wm_img = nib.load(self.inputs.wm_in_dwi)
-        # wm_data = wm_img.get_fdata().astype('bool')
-        # atlas_img = nib.load(dwi_aligned_atlas)
-        # atlas_data = atlas_img.get_fdata().astype('bool')
-        # B0_mask_img = nib.load(B0_mask_tmp_path)
-        # B0_mask_data = B0_mask_img.get_fdata().astype('bool')
-        # wm_data_dil = binary_opening(wm_data, structure=atlas_data,
-        #                              mask=B0_mask_data)
-        # wm_in_dwi_tmp_path = fname_presuffix(
-        #     self.inputs.wm_in_dwi, suffix="_tmp", newpath=runtime.cwd
-        # )
-        # nib.save(nib.Nifti1Image(wm_data_dil, affine=wm_img.affine,
-        #                          header=wm_img.header), wm_in_dwi_tmp_path)
-        #
-        # wm_img.uncache()
-        # atlas_img.uncache()
-        # B0_mask_img.uncache()
-        # del atlas_data, wm_data, B0_mask_data, wm_data_dil
-        # self._results["wm_in_dwi"] = wm_in_dwi_tmp_path
-
-        self._results["wm_in_dwi"] = self.inputs.wm_in_dwi
-        self._results["dwi_aligned_atlas_wmgm_int"] = \
-            dwi_aligned_atlas_wmgm_int
-        self._results["dwi_aligned_atlas"] = dwi_aligned_atlas
-        self._results["aligned_atlas_t1w"] = aligned_atlas_t1w
-        self._results["node_radius"] = self.inputs.node_radius
-        self._results["atlas"] = self.inputs.atlas
-        self._results["parcellation4d"] = parcellation4d_tmp_path
-        self._results["parcellation"] = parcellation_out
-        self._results["coords"] = coords
-        self._results["labels"] = labels
-        self._results["gm_in_dwi"] = self.inputs.gm_in_dwi
-        self._results["vent_csf_in_dwi"] = self.inputs.vent_csf_in_dwi
-        self._results["B0_mask"] = B0_mask_tmp_path
-        self._results["ap_path"] = ap_tmp_path
-        self._results["gtab_file"] = self.inputs.gtab_file
-        self._results["dwi_file"] = self.inputs.dwi_file
-        self._results["waymask_in_dwi"] = waymask_in_dwi
-
-        dir_path = utils.do_dir_path(
-            self.inputs.atlas, os.path.dirname(self.inputs.dwi_file)
-        )
-
-        namer_dir = "{}/tractography".format(dir_path)
-        if not op.isdir(namer_dir):
-            os.mkdir(namer_dir)
-
-        if not os.path.isfile(f"{namer_dir}/"
-                              f"{op.basename(self.inputs.fa_path)}"):
-            copyfile(
-                self.inputs.fa_path,
-                f"{namer_dir}/{op.basename(self.inputs.fa_path)}",
-                copy=True,
-                use_hardlink=False,
-            )
-        if not os.path.isfile(f"{namer_dir}/"
-                              f"{op.basename(self.inputs.ap_path)}"):
-            copyfile(
-                self.inputs.ap_path,
-                f"{namer_dir}/"
-                f"{op.basename(self.inputs.ap_path).replace('_tmp', '')}",
-                copy=True,
-                use_hardlink=False,
-            )
-        if not os.path.isfile(f"{namer_dir}/"
-                              f"{op.basename(self.inputs.B0_mask)}"):
-            copyfile(
-                self.inputs.B0_mask,
-                f"{namer_dir}/"
-                f"{op.basename(self.inputs.B0_mask).replace('_tmp', '')}",
-                copy=True,
-                use_hardlink=False,
-            )
-        if not os.path.isfile(f"{namer_dir}/"
-                              f"{op.basename(self.inputs.gtab_file)}"):
-            copyfile(
-                self.inputs.gtab_file,
-                f"{namer_dir}/{op.basename(self.inputs.gtab_file)}",
-                copy=True,
-                use_hardlink=False,
-            )
-        if not os.path.isfile(f"{namer_dir}/{op.basename(aligned_atlas_t1w)}"):
-            copyfile(
-                aligned_atlas_t1w,
-                f"{namer_dir}/{op.basename(aligned_atlas_t1w)}",
-                copy=True,
-                use_hardlink=False,
-            )
-
-        reg_tmp = [
-            parcellation_tmp_path,
-            mni2t1w_warp_tmp_path,
-            mni2t1_xfm_tmp_path,
-            t1w_brain_mask_tmp_path,
-            t1_aligned_mni_tmp_path,
-            t1w2dwi_bbr_xfm_tmp_path,
-            t1w2dwi_xfm_tmp_path,
-            t1w_brain_tmp_path
-        ]
-        for j in reg_tmp:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        gc.collect()
-
-        return runtime
-
-
-class _RegisterROIDWIInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterROIDWI"""
-
-    dwi_file = File(exists=True, mandatory=True)
-    basedir_path = Directory(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    roi = traits.Any(mandatory=False)
-    fa_path = File(exists=True, mandatory=True)
-    ap_path = File(exists=True, mandatory=True)
-    B0_mask = File(exists=True, mandatory=True)
-    coords = traits.Any(mandatory=True)
-    labels = traits.Any(mandatory=True)
-    gm_in_dwi = File(exists=True, mandatory=True)
-    vent_csf_in_dwi = File(exists=True, mandatory=True)
-    wm_in_dwi = File(exists=True, mandatory=True)
-    gtab_file = File(exists=True, mandatory=True)
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    t1wtissue2dwi_xfm = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    simple = traits.Bool(False, usedefault=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-
-
-class _RegisterROIDWIOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterROIDWI"""
-
-    roi = traits.Any(mandatory=False)
-
-
-class RegisterROIDWI(SimpleInterface):
-    """Interface wrapper for RegisterROIDWI."""
-
-    input_spec = _RegisterROIDWIInputSpec
-    output_spec = _RegisterROIDWIOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import os
-        import time
-        from pynets.registration import utils as regutils
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        import pkg_resources
-
-        template = pkg_resources.resource_filename(
-            "pynets", f"templates/{self.inputs.template_name}_brain_"
-                      f"{self.inputs.vox_size}.nii.gz"
-        )
-
-        template_tmp_path = fname_presuffix(
-            template, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            template,
-            template_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        ap_tmp_path = fname_presuffix(
-            self.inputs.ap_path, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.ap_path,
-            ap_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        roi_file_tmp_path = fname_presuffix(
-            self.inputs.roi, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.roi,
-            roi_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        t1w_brain_tmp_path = fname_presuffix(
-            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w_brain,
-            t1w_brain_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        mni2t1w_warp_tmp_path = fname_presuffix(
-            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1w_warp,
-            mni2t1w_warp_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        t1wtissue2dwi_xfm_tmp_path = fname_presuffix(
-            self.inputs.t1wtissue2dwi_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1wtissue2dwi_xfm,
-            t1wtissue2dwi_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        mni2t1_xfm_tmp_path = fname_presuffix(
-            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1_xfm,
-            mni2t1_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        roi_in_t1w = f"{runtime.cwd}/waymask-" \
-                     f"{os.path.basename(self.inputs.roi).split('.nii')[0]}" \
-                     f"_in_t1w.nii.gz"
-        roi_in_dwi = f"{runtime.cwd}/waymask-" \
-                     f"{os.path.basename(self.inputs.roi).split('.nii')[0]}" \
-                     f"_in_dwi.nii.gz"
-
-        if self.inputs.roi:
-            t1w_brain_tmp_path2 = fname_presuffix(
-                self.inputs.t1w_brain, suffix="2", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.t1w_brain,
-                t1w_brain_tmp_path2,
-                copy=True,
-                use_hardlink=False)
-
-            mni2t1w_warp_tmp_path2 = fname_presuffix(
-                self.inputs.mni2t1w_warp, suffix="2", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mni2t1w_warp,
-                mni2t1w_warp_tmp_path2,
-                copy=True,
-                use_hardlink=False)
-
-            # Align roi
-            roi_in_dwi = regutils.roi2dwi_align(
-                roi_file_tmp_path,
-                t1w_brain_tmp_path2,
-                roi_in_t1w,
-                roi_in_dwi,
-                mni2t1w_warp_tmp_path2,
-                t1wtissue2dwi_xfm_tmp_path,
-                mni2t1_xfm_tmp_path,
-                template_tmp_path,
-                self.inputs.simple,
-            )
-            time.sleep(0.5)
-        else:
-            roi_in_dwi = None
-
-        self._results["roi"] = roi_in_dwi
-
-        reg_tmp = [
-            t1w_brain_tmp_path,
-            mni2t1w_warp_tmp_path,
-            t1wtissue2dwi_xfm_tmp_path,
-            mni2t1_xfm_tmp_path,
-            template_tmp_path,
-            roi_in_t1w,
-            roi_file_tmp_path
-        ]
-        for j in reg_tmp:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        gc.collect()
-
-        return runtime
-
-
-class _RegisterFuncInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterFunc"""
-
-    anat_file = File(exists=True, mandatory=True)
-    mask = traits.Any(mandatory=False)
-    in_dir = traits.Any(mandatory=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    force_create_mask = traits.Bool(True, usedefault=True)
-    simple = traits.Bool(False, usedefault=True)
-    overwrite = traits.Bool(False, usedefault=True)
-
-
-class _RegisterFuncOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterFunc"""
-
-    reg_fmri_complete = traits.Bool()
-    basedir_path = Directory(exists=True, mandatory=True)
-    t1w_brain_mask = traits.Any(mandatory=False)
-    epi_brain_path = traits.Any()
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    t1_aligned_mni = File(exists=True, mandatory=True)
-    gm_mask = File(exists=True, mandatory=True)
-    t1w2mni_xfm = traits.Any()
-    t1w2mni_warp = traits.Any()
-
-
-class RegisterFunc(SimpleInterface):
-    """Interface wrapper for RegisterFunc to create Func->T1w->MNI mappings."""
-
-    input_spec = _RegisterFuncInputSpec
-    output_spec = _RegisterFuncOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import glob
-        import time
-        import os.path as op
-        from pynets.registration import register
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        from pynets.registration.utils import orient_reslice
-
-        anat_mask_existing = [
-            i
-            for i in glob.glob(self.inputs.in_dir +
-                               "/*_desc-brain_mask.nii.gz")
-            if "MNI" not in i
-        ]
-
-        # Copy T1w mask, if provided, else use existing, if detected, else
-        # compute a fresh one
-        if self.inputs.mask:
-            mask_tmp_path = fname_presuffix(
-                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mask,
-                mask_tmp_path,
-                copy=True,
-                use_hardlink=False)
-        else:
-            if len(anat_mask_existing) > 0 and \
-                self.inputs.mask is None and \
-                op.isfile(anat_mask_existing[0]) \
-                    and self.inputs.force_create_mask is False:
-                mask_tmp_path = fname_presuffix(
-                    anat_mask_existing[0], suffix="_tmp",
-                    newpath=runtime.cwd
-                )
-                copyfile(
-                    anat_mask_existing[0],
-                    mask_tmp_path,
-                    copy=True,
-                    use_hardlink=False)
-                mask_tmp_path = orient_reslice(
-                    mask_tmp_path, runtime.cwd, self.inputs.vox_size
-                )
-            else:
-                mask_tmp_path = None
-
-        gm_mask_existing = glob.glob(
-            self.inputs.in_dir +
-            "/*_label-GM_probseg.nii.gz")
-        if len(gm_mask_existing) > 0:
-            gm_mask = fname_presuffix(gm_mask_existing[0],
-                                      newpath=runtime.cwd)
-            copyfile(
-                gm_mask_existing[0],
-                gm_mask,
-                copy=True,
-                use_hardlink=False)
-            gm_mask = orient_reslice(
-                gm_mask, runtime.cwd, self.inputs.vox_size
-            )
-        else:
-            gm_mask = None
-
-        wm_mask_existing = glob.glob(
-            self.inputs.in_dir +
-            "/*_label-WM_probseg.nii.gz")
-        if len(wm_mask_existing) > 0:
-            wm_mask = fname_presuffix(wm_mask_existing[0],
-                                      newpath=runtime.cwd)
-            copyfile(
-                wm_mask_existing[0],
-                wm_mask,
-                copy=True,
-                use_hardlink=False)
-            wm_mask = orient_reslice(
-                wm_mask, runtime.cwd, self.inputs.vox_size
-            )
-        else:
-            wm_mask = None
-
-        anat_file_tmp_path = fname_presuffix(
-            self.inputs.anat_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.anat_file,
-            anat_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        reg = register.FmriReg(
-            basedir_path=runtime.cwd,
-            anat_file=anat_file_tmp_path,
-            vox_size=self.inputs.vox_size,
-            template_name=self.inputs.template_name,
-            simple=self.inputs.simple,
-        )
-
-        # Generate T1w brain mask
-        reg.gen_mask(mask_tmp_path)
-
-        # Perform anatomical segmentation
-        reg.gen_tissue(wm_mask, gm_mask, self.inputs.overwrite)
-
-        # Align t1w to mni template
-        # from joblib import Memory
-        # import os
-        # location = f"{outdir}/joblib_" \
-        #            f"{self.inputs.anat_file.split('.nii')[0]}"
-        # os.makedirs(location, exist_ok=True)
-        # memory = Memory(location)
-        # t1w2mni_align = memory.cache(reg.t1w2mni_align)
-        # t1w2mni_align()
-        reg.t1w2mni_align()
-        time.sleep(0.5)
-
-        self._results["reg_fmri_complete"] = True
-        self._results["basedir_path"] = runtime.cwd
-        self._results["t1w_brain_mask"] = reg.t1w_brain_mask
-        self._results["t1w_brain"] = reg.t1w_brain
-        self._results["mni2t1w_warp"] = reg.mni2t1w_warp
-        self._results["mni2t1_xfm"] = reg.mni2t1_xfm
-        self._results["t1_aligned_mni"] = reg.t1_aligned_mni
-        self._results["gm_mask"] = reg.gm_mask
-        self._results["t1w2mni_warp"] = reg.warp_t1w2mni
-        self._results["t1w2mni_xfm"] = reg.t12mni_xfm
-
-        gc.collect()
-
-        return runtime
-
-
-class _RegisterParcellation2MNIFuncInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterParcellation2MNIFunc"""
-
-    parcellation = traits.Any(mandatory=False)
-    t1w2mni_xfm = File(exists=True, mandatory=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    t1w_brain = File(exists=True, mandatory=True)
-    t1w2mni_warp = traits.Any(mandatory=True)
-    dir_path = traits.Any(mandatory=True)
-    simple = traits.Bool(False, usedefault=True)
-
-
-class _RegisterParcellation2MNIFuncOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterParcellation2MNIFunc"""
-
-    aligned_atlas_mni = File(exists=True, mandatory=True)
-
-
-class RegisterParcellation2MNIFunc(SimpleInterface):
-    """Interface wrapper for RegisterParcellation2MNIFunc."""
-
-    input_spec = _RegisterParcellation2MNIFuncInputSpec
-    output_spec = _RegisterParcellation2MNIFuncOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import os
-        import pkg_resources
-        import time
-        from pynets.core.utils import prune_suffices
-        from pynets.registration import utils as regutils
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-
-        template = pkg_resources.resource_filename(
-            "pynets", f"templates/{self.inputs.template_name}_brain_"
-                      f"{self.inputs.vox_size}.nii.gz"
-        )
-        template_mask = pkg_resources.resource_filename(
-            "pynets", f"templates/{self.inputs.template_name}_"
-                      f"brain_mask_{self.inputs.vox_size}.nii.gz"
-        )
-
-        template_tmp_path = fname_presuffix(
-            template, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            template,
-            template_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        template_mask_tmp_path = fname_presuffix(
-            template_mask, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            template_mask,
-            template_mask_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        t1w_brain_tmp_path = fname_presuffix(
-            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w_brain,
-            t1w_brain_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        parcellation_tmp_path = fname_presuffix(
-            self.inputs.parcellation, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.parcellation,
-            parcellation_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        atlas_name = prune_suffices(os.path.basename(self.inputs.parcellation
-                                                     ).split('.nii')[0])
-
-        base_dir_tmp = f"{runtime.cwd}/atlas_{atlas_name}"
-
-        os.makedirs(base_dir_tmp, exist_ok=True)
-
-        aligned_atlas_t1w = f"{base_dir_tmp}{'/'}{atlas_name}_" \
-                            f"t1w_res.nii.gz"
-
-        aligned_atlas_mni = f"{base_dir_tmp}{'/'}{atlas_name}_" \
-                            f"t1w_mni.nii.gz"
-
-        t1w2mni_xfm_tmp_path = fname_presuffix(
-            self.inputs.t1w2mni_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w2mni_xfm,
-            t1w2mni_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        t1w2mni_warp_tmp_path = fname_presuffix(
-            self.inputs.t1w2mni_warp, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w2mni_warp,
-            t1w2mni_warp_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        aligned_atlas_mni = regutils.RegisterParcellation2MNIFunc_align(
-            parcellation_tmp_path,
-            template_tmp_path,
-            template_mask_tmp_path,
-            t1w_brain_tmp_path,
-            t1w2mni_xfm_tmp_path,
-            aligned_atlas_t1w,
-            aligned_atlas_mni,
-            t1w2mni_warp_tmp_path,
-            self.inputs.simple
-        )
-        time.sleep(0.5)
-
-        out_dir = f"{self.inputs.dir_path}/t1w_clustered_parcellations/"
-        os.makedirs(out_dir, exist_ok=True)
-        copyfile(
-            aligned_atlas_t1w,
-            f"{out_dir}{atlas_name}_t1w_skull.nii.gz",
-            copy=True,
-            use_hardlink=False)
-
-        self._results["aligned_atlas_mni"] = aligned_atlas_mni
-
-        reg_tmp = [
-            parcellation_tmp_path,
-            template_tmp_path,
-            t1w2mni_xfm_tmp_path,
-            t1w2mni_warp_tmp_path,
-            template_mask_tmp_path
-        ]
-
-        for j in reg_tmp:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        gc.collect()
-
-        return runtime
-
-
-class _RegisterAtlasFuncInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterAtlasFunc"""
-
-    atlas = traits.Any(mandatory=False)
-    subnet = traits.Any(mandatory=True)
-    parcellation4d = traits.Any(mandatory=True)
-    parcellation = traits.Any(mandatory=False)
-    basedir_path = Directory(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    t1w_brain_mask = File(exists=True, mandatory=True)
-    t1_aligned_mni = File(exists=True, mandatory=True)
-    gm_mask = File(exists=True, mandatory=True)
-    coords = traits.Any(mandatory=True)
-    labels = traits.Any(mandatory=True)
-    node_radius = traits.Any(mandatory=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    reg_fmri_complete = traits.Bool()
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    already_run = traits.Bool(False, usedefault=True)
-    dir_path = traits.Any(mandatory=True)
-    simple = traits.Bool(False, usedefault=True)
-
-
-class _RegisterAtlasFuncOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterAtlasFunc"""
-
-    aligned_atlas_gm = File(exists=True, mandatory=True)
-    coords = traits.Any(mandatory=True)
-    labels = traits.Any(mandatory=True)
-    node_radius = traits.Any()
-    atlas = traits.Any()
-
-
-class RegisterAtlasFunc(SimpleInterface):
-    """Interface wrapper for RegisterAtlasFunc."""
-
-    input_spec = _RegisterAtlasFuncInputSpec
-    output_spec = _RegisterAtlasFuncOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import os
-        import time
-        import glob
-        from pynets.registration import utils as regutils
-        from pynets.core.nodemaker import \
-            drop_coords_labels_from_restricted_parcellation
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-
-        if self.inputs.subnet:
-            atlas_name = f"{self.inputs.atlas}_{self.inputs.subnet}"
-        else:
-            atlas_name = f"{self.inputs.atlas}"
-
-        if self.inputs.already_run is True:
-            try:
-                aligned_atlas_gm = [i for i in
-                                    glob.glob(f"{self.inputs.dir_path}/"
-                                              f"t1w_clustered_parcellations/"
-                                              f"*.nii.gz") if
-                                    atlas_name.strip('_mni') in i][0]
-
-                # Correct coords and labels
-                [aligned_atlas_gm, coords, labels] = \
-                    drop_coords_labels_from_restricted_parcellation(
-                        aligned_atlas_gm, self.inputs.coords,
-                        self.inputs.labels)
-
-            except FileNotFoundError as e:
-                print(e, 'T1w-space parcellation not found. Did you delete '
-                      'outputs?')
-        else:
-            if self.inputs.parcellation is None:
-                parcellation_tmp_path = None
-            else:
-                parcellation_tmp_path = fname_presuffix(
-                    self.inputs.parcellation, suffix="_tmp", newpath=runtime.cwd
-                )
-                copyfile(
-                    self.inputs.parcellation,
-                    parcellation_tmp_path,
-                    copy=True,
-                    use_hardlink=False)
-
-            if self.inputs.parcellation4d is None:
-                parcellation4d_tmp_path = None
-            else:
-                parcellation4d_tmp_path = fname_presuffix(
-                    self.inputs.parcellation4d, suffix="_tmp",
-                    newpath=runtime.cwd
-                )
-                copyfile(
-                    self.inputs.parcellation4d,
-                    parcellation4d_tmp_path,
-                    copy=True,
-                    use_hardlink=False,
-                )
-
-            t1w_brain_tmp_path = fname_presuffix(
-                self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.t1w_brain,
-                t1w_brain_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            t1w_brain_mask_tmp_path = fname_presuffix(
-                self.inputs.t1w_brain_mask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.t1w_brain_mask,
-                t1w_brain_mask_tmp_path,
-                copy=True,
-                use_hardlink=False,
-            )
-
-            t1_aligned_mni_tmp_path = fname_presuffix(
-                self.inputs.t1_aligned_mni, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.t1_aligned_mni,
-                t1_aligned_mni_tmp_path,
-                copy=True,
-                use_hardlink=False,
-            )
-
-            gm_mask_tmp_path = fname_presuffix(
-                self.inputs.gm_mask, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.gm_mask,
-                gm_mask_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            base_dir_tmp = f"{runtime.cwd}/atlas_{atlas_name}"
-            os.makedirs(base_dir_tmp, exist_ok=True)
-
-            aligned_atlas_t1mni = f"{base_dir_tmp}{'/'}{atlas_name}_" \
-                                  f"t1w_mni.nii.gz"
-            aligned_atlas_skull = f"{base_dir_tmp}{'/'}{atlas_name}_" \
-                                  f"t1w_skull.nii.gz"
-            aligned_atlas_gm = f"{base_dir_tmp}{'/'}{atlas_name}{'_gm.nii.gz'}"
-
-            if self.inputs.node_radius is not None:
-                atlas_name = f"{atlas_name}{'_'}{self.inputs.node_radius}"
-
-            mni2t1_xfm_tmp_path = fname_presuffix(
-                self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mni2t1_xfm,
-                mni2t1_xfm_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            mni2t1w_warp_tmp_path = fname_presuffix(
-                self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.mni2t1w_warp,
-                mni2t1w_warp_tmp_path,
-                copy=True,
-                use_hardlink=False,
-            )
-
-            aligned_atlas_gm, aligned_atlas_skull = regutils.atlas2t1w_align(
-                parcellation_tmp_path,
-                parcellation4d_tmp_path,
-                atlas_name,
-                t1w_brain_tmp_path,
-                t1w_brain_mask_tmp_path,
-                t1_aligned_mni_tmp_path,
-                mni2t1w_warp_tmp_path,
-                mni2t1_xfm_tmp_path,
-                gm_mask_tmp_path,
-                aligned_atlas_t1mni,
-                aligned_atlas_skull,
-                aligned_atlas_gm,
-                self.inputs.simple,
-            )
-            time.sleep(0.5)
-
-            # Correct coords and labels
-            [aligned_atlas_gm, coords, labels] = \
-                drop_coords_labels_from_restricted_parcellation(
-                aligned_atlas_gm, self.inputs.coords, self.inputs.labels)
-
-            reg_tmp = [
-                parcellation4d_tmp_path,
-                parcellation_tmp_path,
-                gm_mask_tmp_path,
-                t1_aligned_mni_tmp_path,
-                t1w_brain_mask_tmp_path,
-                mni2t1w_warp_tmp_path,
-                mni2t1_xfm_tmp_path,
-            ]
-
-            if self.inputs.parcellation is None:
-                parcellation_out = self.inputs.parcellation4d
-                copyfile(
-                    aligned_atlas_gm,
-                    f"{os.path.dirname(parcellation_out)}/"
-                    f"{os.path.basename(aligned_atlas_gm)}",
-                    copy=True,
-                    use_hardlink=False,
-                )
-            else:
-                parcellation_out = self.inputs.parcellation
-                copyfile(
-                    aligned_atlas_gm,
-                    f"{os.path.dirname(parcellation_out)}/parcellations/"
-                    f"{os.path.basename(aligned_atlas_gm)}",
-                    copy=True,
-                    use_hardlink=False,
-                )
-
-            for j in reg_tmp:
-                if j is not None:
-                    if os.path.isfile(j):
-                        os.system(f"rm -f {j} &")
-
-        # Use for debugging check
-        parcellation_img = nib.load(aligned_atlas_gm)
-
-        intensities = [i for i in list(np.unique(
-            np.asarray(
-                parcellation_img.dataobj).astype("int"))
-        ) if i != 0]
-        try:
-            assert len(coords) == len(labels) == len(intensities)
-        except ValueError as e:
-            print(e, 'Failed!')
-            print(f"# Coords: {len(coords)}")
-            print(f"# Labels: {len(labels)}")
-            print(f"# Intensities: {len(intensities)}")
-
-        self._results["aligned_atlas_gm"] = aligned_atlas_gm
-        self._results["coords"] = coords
-        self._results["labels"] = labels
-        self._results["atlas"] = self.inputs.atlas
-        self._results["node_radius"] = self.inputs.node_radius
-
-        gc.collect()
-
-        return runtime
-
-
-class _RegisterROIEPIInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for RegisterROIEPI"""
-
-    basedir_path = Directory(exists=True, mandatory=True)
-    anat_file = File(exists=True, mandatory=True)
-    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
-    roi = traits.Any(mandatory=False)
-    t1w_brain = File(exists=True, mandatory=True)
-    mni2t1w_warp = File(exists=True, mandatory=True)
-    mni2t1_xfm = File(exists=True, mandatory=True)
-    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
-    simple = traits.Bool(False, usedefault=True)
-
-
-class _RegisterROIEPIOutputSpec(TraitedSpec):
-    """Output interface wrapper for RegisterROIEPI"""
-
-    roi = traits.Any(mandatory=False)
-
-
-class RegisterROIEPI(SimpleInterface):
-    """Interface wrapper for RegisterROIEPI."""
-
-    input_spec = _RegisterROIEPIInputSpec
-    output_spec = _RegisterROIEPIOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import os
-        import time
-        from pynets.registration import utils as regutils
-        from nipype.utils.filemanip import fname_presuffix, copyfile
-        import pkg_resources
-
-        template = pkg_resources.resource_filename(
-            "pynets", f"templates/{self.inputs.template_name}_brain_"
-                      f"{self.inputs.vox_size}.nii.gz"
-        )
-        template_tmp_path = fname_presuffix(
-            template, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            template,
-            template_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        roi_file_tmp_path = fname_presuffix(
-            self.inputs.roi, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.roi,
-            roi_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        roi_in_t1w = f"{runtime.cwd}/roi-" \
-                     f"{os.path.basename(self.inputs.roi).split('.nii')[0]}" \
-                     f"_in_t1w.nii.gz"
-
-        t1w_brain_tmp_path = fname_presuffix(
-            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.t1w_brain,
-            t1w_brain_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        mni2t1w_warp_tmp_path = fname_presuffix(
-            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1w_warp,
-            mni2t1w_warp_tmp_path,
-            copy=True,
-            use_hardlink=False,
-        )
-
-        mni2t1_xfm_tmp_path = fname_presuffix(
-            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.mni2t1_xfm,
-            mni2t1_xfm_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        if self.inputs.roi:
-            # Align roi
-            roi_in_t1w = regutils.roi2t1w_align(
-                roi_file_tmp_path,
-                t1w_brain_tmp_path,
-                mni2t1_xfm_tmp_path,
-                mni2t1w_warp_tmp_path,
-                roi_in_t1w,
-                template_tmp_path,
-                self.inputs.simple,
-            )
-            time.sleep(0.5)
-        else:
-            roi_in_t1w = None
-
-        self._results["roi"] = roi_in_t1w
-
-        reg_tmp = [
-            t1w_brain_tmp_path,
-            mni2t1w_warp_tmp_path,
-            mni2t1_xfm_tmp_path,
-            roi_file_tmp_path,
-            template_tmp_path
-        ]
-        for j in reg_tmp:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-        gc.collect()
-
-        return runtime
-
-
-class _TrackingInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for Tracking"""
-
-    B0_mask = File(exists=True, mandatory=True)
-    gm_in_dwi = File(exists=True, mandatory=True)
-    vent_csf_in_dwi = File(exists=True, mandatory=True)
-    wm_in_dwi = File(exists=True, mandatory=True)
-    tiss_class = traits.Str(mandatory=True)
-    labels_im_file_wm_gm_int = File(exists=True, mandatory=True)
-    labels_im_file = File(exists=True, mandatory=True)
-    target_samples = traits.Any(mandatory=True)
-    curv_thr_list = traits.List(mandatory=True)
-    step_list = traits.List(mandatory=True)
-    track_type = traits.Str(mandatory=True)
-    min_length = traits.Any(mandatory=True)
-    maxcrossing = traits.Any(mandatory=True)
-    directget = traits.Str(mandatory=True)
-    conn_model = traits.Str(mandatory=True)
-    gtab_file = File(exists=True, mandatory=True)
-    dwi_file = File(exists=True, mandatory=True)
-    subnet = traits.Any(mandatory=False)
-    node_radius = traits.Any(mandatory=True)
-    dens_thresh = traits.Bool(False, mandatory=False)
-    ID = traits.Any(mandatory=True)
-    roi = traits.Any(mandatory=False)
-    min_span_tree = traits.Bool(False, mandatory=False)
-    disp_filt = traits.Bool(False, mandatory=False)
-    parc = traits.Bool(mandatory=True)
-    prune = traits.Any(mandatory=True)
-    atlas = traits.Any(mandatory=False)
-    parcellation = traits.Any(mandatory=False)
-    labels = traits.Any(mandatory=True)
-    coords = traits.Any(mandatory=True)
-    norm = traits.Any(mandatory=True)
-    binary = traits.Bool(False, usedefault=True)
-    atlas_t1w = File(exists=True, mandatory=True)
-    fa_path = File(exists=True, mandatory=True)
-    waymask = traits.Any(mandatory=False)
-    t1w2dwi = File(exists=True, mandatory=True)
-
-
-class _TrackingOutputSpec(TraitedSpec):
-    """Output interface wrapper for Tracking"""
-
-    streams = traits.Any()
-    track_type = traits.Str(mandatory=True)
-    target_samples = traits.Any(mandatory=True)
-    conn_model = traits.Str(mandatory=True)
-    dir_path = Directory(exists=True, mandatory=True)
-    subnet = traits.Any(mandatory=False)
-    node_radius = traits.Any()
-    dens_thresh = traits.Bool(False, mandatory=False)
-    ID = traits.Any(mandatory=True)
-    roi = traits.Any(mandatory=False)
-    min_span_tree = traits.Bool(False, mandatory=False)
-    disp_filt = traits.Bool(False, mandatory=False)
-    parc = traits.Bool()
-    prune = traits.Any()
-    atlas = traits.Any(mandatory=False)
-    parcellation = traits.Any(mandatory=False)
-    labels = traits.Any(mandatory=True)
-    coords = traits.Any(mandatory=True)
-    norm = traits.Any()
-    binary = traits.Bool(False, usedefault=True)
-    atlas_t1w = File(exists=True, mandatory=True)
-    curv_thr_list = traits.List(mandatory=True)
-    step_list = traits.List(mandatory=True)
-    fa_path = File(exists=True, mandatory=True)
-    dm_path = traits.Any()
-    directget = traits.Str(mandatory=True)
-    labels_im_file = File(exists=True, mandatory=True)
-    min_length = traits.Any()
-
-
-class Tracking(SimpleInterface):
-    """Interface wrapper for Tracking"""
-
-    input_spec = _TrackingInputSpec
-    output_spec = _TrackingOutputSpec
-
-    def _run_interface(self, runtime):
-        import gc
-        import os
-        import time
-        import os.path as op
-        from dipy.io import load_pickle
-        from colorama import Fore, Style
-        from dipy.data import get_sphere
-        from pynets.core import utils
-        from pynets.core.utils import load_runconfig
-        from pynets.dmri.track import (
-            reconstruction,
-            create_density_map,
-            track_ensemble,
-        )
-        from dipy.io.stateful_tractogram import Space, StatefulTractogram, \
-            Origin
-        from dipy.io.streamline import save_tractogram
-        from nipype.utils.filemanip import copyfile, fname_presuffix
-
-        hardcoded_params = load_runconfig()
-        use_life = hardcoded_params['tracking']["use_life"][0]
-        roi_neighborhood_tol = hardcoded_params['tracking'][
-            "roi_neighborhood_tol"][0]
-        sphere = hardcoded_params['tracking']["sphere"][0]
-
-        dir_path = utils.do_dir_path(
-            self.inputs.atlas, os.path.dirname(self.inputs.dwi_file)
-        )
-
-        namer_dir = "{}/tractography".format(dir_path)
-        if not os.path.isdir(namer_dir):
-            os.makedirs(namer_dir, exist_ok=True)
-
-        # Load diffusion data
-        dwi_file_tmp_path = fname_presuffix(
-            self.inputs.dwi_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.dwi_file,
-            dwi_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        dwi_img = nib.load(dwi_file_tmp_path, mmap=True)
-        dwi_data = dwi_img.get_fdata(dtype=np.float32)
-
-        # Load FA data
-        fa_file_tmp_path = fname_presuffix(
-            self.inputs.fa_path, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.fa_path,
-            fa_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        fa_img = nib.load(fa_file_tmp_path, mmap=True)
-
-        labels_im_file_tmp_path = fname_presuffix(
-            self.inputs.labels_im_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.labels_im_file,
-            labels_im_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        # Load B0 mask
-        B0_mask_tmp_path = fname_presuffix(
-            self.inputs.B0_mask, suffix="_tmp",
-            newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.B0_mask,
-            B0_mask_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        streams = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % (
-            runtime.cwd,
-            "/streamlines_",
-            "%s"
-            % (self.inputs.subnet + "_" if self.inputs.subnet is not None
-               else ""),
-            "%s"
-            % (
-                op.basename(self.inputs.roi).split(".")[0] + "_"
-                if self.inputs.roi is not None
-                else ""
-            ),
-            self.inputs.conn_model,
-            "_",
-            self.inputs.target_samples,
-            "_",
-            "%s"
-            % (
-                "%s%s" % (self.inputs.node_radius, "mm_")
-                if (
-                    (self.inputs.node_radius != "parc")
-                    and (self.inputs.node_radius is not None)
-                )
-                else "parc_"
-            ),
-            "curv-",
-            str(self.inputs.curv_thr_list).replace(", ", "_"),
-            "_step-",
-            str(self.inputs.step_list).replace(", ", "_"),
-            "_directget-",
-            self.inputs.directget,
-            "_minlength-",
-            self.inputs.min_length,
-            ".trk",
-        )
-
-        if os.path.isfile(f"{namer_dir}/{op.basename(streams)}"):
-            from dipy.io.streamline import load_tractogram
-            copyfile(
-                f"{namer_dir}/{op.basename(streams)}",
-                streams,
-                copy=True,
-                use_hardlink=False,
-            )
-            tractogram = load_tractogram(
-                streams,
-                fa_img,
-                bbox_valid_check=False,
-            )
-
-            streamlines = tractogram.streamlines
-
-            # Create streamline density map
-            try:
-                [dir_path, dm_path] = create_density_map(
-                    fa_img,
-                    dir_path,
-                    streamlines,
-                    self.inputs.conn_model,
-                    self.inputs.target_samples,
-                    self.inputs.node_radius,
-                    self.inputs.curv_thr_list,
-                    self.inputs.step_list,
-                    self.inputs.subnet,
-                    self.inputs.roi,
-                    self.inputs.directget,
-                    self.inputs.min_length,
-                    namer_dir,
-                )
-            except BaseException:
-                print('Density map failed. Check tractography output.')
-                dm_path = None
-
-            del streamlines, tractogram
-            fa_img.uncache()
-            dwi_img.uncache()
-            gc.collect()
-            self._results["dm_path"] = dm_path
-            self._results["streams"] = streams
-            recon_path = None
-        else:
-            # Fit diffusion model
-            # Save reconstruction to .npy
-            recon_path = "%s%s%s%s%s%s%s%s" % (
-                runtime.cwd,
-                "/reconstruction_",
-                "%s"
-                % (self.inputs.subnet + "_" if self.inputs.subnet is not None
-                   else ""),
-                "%s"
-                % (
-                    op.basename(self.inputs.roi).split(".")[0] + "_"
-                    if self.inputs.roi is not None
-                    else ""
-                ),
-                self.inputs.conn_model,
-                "_",
-                "%s"
-                % (
-                    "%s%s" % (self.inputs.node_radius, "mm")
-                    if (
-                        (self.inputs.node_radius != "parc")
-                        and (self.inputs.node_radius is not None)
-                    )
-                    else "parc"
-                ),
-                ".hdf5",
-            )
-
-            gtab_file_tmp_path = fname_presuffix(
-                self.inputs.gtab_file, suffix="_tmp", newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.gtab_file,
-                gtab_file_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            gtab = load_pickle(gtab_file_tmp_path)
-
-            # Only re-run the reconstruction if we have to
-            if not os.path.isfile(f"{namer_dir}/{op.basename(recon_path)}"):
-                import h5py
-                model, _ = reconstruction(
-                    self.inputs.conn_model,
-                    gtab,
-                    dwi_data,
-                    B0_mask_tmp_path,
-                )
-                with h5py.File(recon_path, 'w') as hf:
-                    hf.create_dataset("reconstruction",
-                                      data=model.astype('float32'), dtype='f4')
-                hf.close()
-
-                copyfile(
-                    recon_path,
-                    f"{namer_dir}/{op.basename(recon_path)}",
-                    copy=True,
-                    use_hardlink=False,
-                )
-                time.sleep(2)
-                del model
-            elif os.path.getsize(f"{namer_dir}/{op.basename(recon_path)}") > 0:
-                print(
-                    f"Found existing reconstruction with "
-                    f"{self.inputs.conn_model}. Loading...")
-                copyfile(
-                    f"{namer_dir}/{op.basename(recon_path)}",
-                    recon_path,
-                    copy=True,
-                    use_hardlink=False,
-                )
-                time.sleep(5)
-            else:
-                import h5py
-                model, _ = reconstruction(
-                    self.inputs.conn_model,
-                    gtab,
-                    dwi_data,
-                    B0_mask_tmp_path,
-                )
-                with h5py.File(recon_path, 'w') as hf:
-                    hf.create_dataset("reconstruction",
-                                      data=model.astype('float32'), dtype='f4')
-                hf.close()
-
-                copyfile(
-                    recon_path,
-                    f"{namer_dir}/{op.basename(recon_path)}",
-                    copy=True,
-                    use_hardlink=False,
-                )
-                time.sleep(5)
-                del model
-            dwi_img.uncache()
-            del dwi_data
-
-            # Load atlas wm-gm interface reduced version for seeding
-            labels_im_file_tmp_path_wm_gm_int = fname_presuffix(
-                self.inputs.labels_im_file_wm_gm_int, suffix="_tmp",
-                newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.labels_im_file_wm_gm_int,
-                labels_im_file_tmp_path_wm_gm_int,
-                copy=True,
-                use_hardlink=False)
-
-            t1w2dwi_tmp_path = fname_presuffix(
-                self.inputs.t1w2dwi, suffix="_tmp",
-                newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.t1w2dwi,
-                t1w2dwi_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            gm_in_dwi_tmp_path = fname_presuffix(
-                self.inputs.gm_in_dwi, suffix="_tmp",
-                newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.gm_in_dwi,
-                gm_in_dwi_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            vent_csf_in_dwi_tmp_path = fname_presuffix(
-                self.inputs.vent_csf_in_dwi, suffix="_tmp",
-                newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.vent_csf_in_dwi,
-                vent_csf_in_dwi_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            wm_in_dwi_tmp_path = fname_presuffix(
-                self.inputs.wm_in_dwi, suffix="_tmp",
-                newpath=runtime.cwd
-            )
-            copyfile(
-                self.inputs.wm_in_dwi,
-                wm_in_dwi_tmp_path,
-                copy=True,
-                use_hardlink=False)
-
-            if self.inputs.waymask:
-                waymask_tmp_path = fname_presuffix(
-                    self.inputs.waymask, suffix="_tmp",
-                    newpath=runtime.cwd
-                )
-                copyfile(
-                    self.inputs.waymask,
-                    waymask_tmp_path,
-                    copy=True,
-                    use_hardlink=False)
-            else:
-                waymask_tmp_path = None
-
-            # Iteratively build a list of streamlines for each ROI while
-            # tracking
-            print(
-                f"{Fore.GREEN}Target streamlines per iteration: "
-                f"{Fore.BLUE} "
-                f"{self.inputs.target_samples}"
-            )
-            print(Style.RESET_ALL)
-            print(
-                f"{Fore.GREEN}Curvature threshold(s): {Fore.BLUE} "
-                f"{self.inputs.curv_thr_list}"
-            )
-            print(Style.RESET_ALL)
-            print(f"{Fore.GREEN}Step size(s): {Fore.BLUE} "
-                  f"{self.inputs.step_list}")
-            print(Style.RESET_ALL)
-            print(f"{Fore.GREEN}Tracking type: {Fore.BLUE} "
-                  f"{self.inputs.track_type}")
-            print(Style.RESET_ALL)
-            if self.inputs.directget == "prob":
-                print(f"{Fore.GREEN}Direction-getting type: {Fore.BLUE}"
-                      f"Probabilistic")
-            elif self.inputs.directget == "clos":
-                print(f"{Fore.GREEN}Direction-getting type: "
-                      f"{Fore.BLUE}Closest Peak")
-            elif self.inputs.directget == "det":
-                print(
-                    f"{Fore.GREEN}Direction-getting type: "
-                    f"{Fore.BLUE}Deterministic Maximum"
-                )
-            else:
-                raise ValueError("Direction-getting type not recognized!")
-
-            print(Style.RESET_ALL)
-
-            # Commence Ensemble Tractography
-            try:
-                streamlines = track_ensemble(
-                    self.inputs.target_samples,
-                    labels_im_file_tmp_path_wm_gm_int,
-                    labels_im_file_tmp_path,
-                    recon_path,
-                    get_sphere(sphere),
-                    self.inputs.directget,
-                    self.inputs.curv_thr_list,
-                    self.inputs.step_list,
-                    self.inputs.track_type,
-                    self.inputs.maxcrossing,
-                    int(roi_neighborhood_tol),
-                    self.inputs.min_length,
-                    waymask_tmp_path,
-                    B0_mask_tmp_path,
-                    t1w2dwi_tmp_path, gm_in_dwi_tmp_path,
-                    vent_csf_in_dwi_tmp_path, wm_in_dwi_tmp_path,
-                    self.inputs.tiss_class
-                )
-                gc.collect()
-            except BaseException as w:
-                print(f"\n{Fore.RED}Tractography failed: {w}")
-                print(Style.RESET_ALL)
-                streamlines = None
-
-            if streamlines is not None:
-                # import multiprocessing
-                # from pynets.core.utils import kill_process_family
-                # return kill_process_family(int(
-                # multiprocessing.current_process().pid))
-
-                # Linear Fascicle Evaluation (LiFE)
-                if use_life is True:
-                    print('Using LiFE to evaluate streamline plausibility...')
-                    from pynets.dmri.utils import \
-                        evaluate_streamline_plausibility
-                    dwi_img = nib.load(dwi_file_tmp_path)
-                    dwi_data = dwi_img.get_fdata(dtype=np.float32)
-                    orig_count = len(streamlines)
-
-                    if self.inputs.waymask:
-                        mask_data = nib.load(
-                            waymask_tmp_path).get_fdata().astype(
-                            'bool').astype('int')
-                    else:
-                        mask_data = nib.load(
-                            wm_in_dwi_tmp_path).get_fdata().astype(
-                            'bool').astype('int')
-                    try:
-                        streamlines = evaluate_streamline_plausibility(
-                            dwi_data, gtab, mask_data, streamlines,
-                            sphere=sphere)
-                    except BaseException:
-                        print(f"Linear Fascicle Evaluation failed. "
-                              f"Visually checking streamlines output "
-                              f"{namer_dir}/{op.basename(streams)} is "
-                              f"recommended.")
-                    if len(streamlines) < 0.5*orig_count:
-                        raise ValueError('LiFE revealed no plausible '
-                                         'streamlines in the tractogram!')
-                    del dwi_data, mask_data
-
-                # Save streamlines to trk
-                stf = StatefulTractogram(
-                    streamlines,
-                    fa_img,
-                    origin=Origin.NIFTI,
-                    space=Space.VOXMM)
-                stf.remove_invalid_streamlines()
-
-                save_tractogram(
-                    stf,
-                    streams,
-                )
-
-                del stf
-
-                copyfile(
-                    streams,
-                    f"{namer_dir}/{op.basename(streams)}",
-                    copy=True,
-                    use_hardlink=False,
-                )
-
-                # Create streamline density map
-                try:
-                    [dir_path, dm_path] = create_density_map(
-                        dwi_img,
-                        dir_path,
-                        streamlines,
-                        self.inputs.conn_model,
-                        self.inputs.target_samples,
-                        self.inputs.node_radius,
-                        self.inputs.curv_thr_list,
-                        self.inputs.step_list,
-                        self.inputs.subnet,
-                        self.inputs.roi,
-                        self.inputs.directget,
-                        self.inputs.min_length,
-                        namer_dir,
-                    )
-                except BaseException:
-                    print('Density map failed. Check tractography output.')
-                    dm_path = None
-
-                del streamlines
-                dwi_img.uncache()
-                gc.collect()
-                self._results["dm_path"] = dm_path
-                self._results["streams"] = streams
-            else:
-                self._results["streams"] = None
-                self._results["dm_path"] = None
-            tmp_files = [gtab_file_tmp_path,
-                         wm_in_dwi_tmp_path, gm_in_dwi_tmp_path,
-                         vent_csf_in_dwi_tmp_path, t1w2dwi_tmp_path]
-
-            for j in tmp_files:
-                if j is not None:
-                    if os.path.isfile(j):
-                        os.system(f"rm -f {j} &")
-
-        self._results["track_type"] = self.inputs.track_type
-        self._results["target_samples"] = self.inputs.target_samples
-        self._results["conn_model"] = self.inputs.conn_model
-        self._results["dir_path"] = dir_path
-        self._results["subnet"] = self.inputs.subnet
-        self._results["node_radius"] = self.inputs.node_radius
-        self._results["dens_thresh"] = self.inputs.dens_thresh
-        self._results["ID"] = self.inputs.ID
-        self._results["roi"] = self.inputs.roi
-        self._results["min_span_tree"] = self.inputs.min_span_tree
-        self._results["disp_filt"] = self.inputs.disp_filt
-        self._results["parc"] = self.inputs.parc
-        self._results["prune"] = self.inputs.prune
-        self._results["atlas"] = self.inputs.atlas
-        self._results["parcellation"] = self.inputs.parcellation
-        self._results["labels"] = self.inputs.labels
-        self._results["coords"] = self.inputs.coords
-        self._results["norm"] = self.inputs.norm
-        self._results["binary"] = self.inputs.binary
-        self._results["atlas_t1w"] = self.inputs.atlas_t1w
-        self._results["curv_thr_list"] = self.inputs.curv_thr_list
-        self._results["step_list"] = self.inputs.step_list
-        self._results["fa_path"] = fa_file_tmp_path
-        self._results["directget"] = self.inputs.directget
-        self._results["labels_im_file"] = labels_im_file_tmp_path
-        self._results["min_length"] = self.inputs.min_length
-
-        tmp_files = [B0_mask_tmp_path, dwi_file_tmp_path]
-
-        for j in tmp_files:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        # Exercise caution when deleting copied recon_path
-        # if recon_path is not None:
-        #     if os.path.isfile(recon_path):
-        #         os.remove(recon_path)
-
-        return runtime
-
-
-class _MakeGtabBmaskInputSpec(BaseInterfaceInputSpec):
-    """Input interface wrapper for MakeGtabBmask"""
-
-    dwi_file = File(exists=True, mandatory=True)
-    fbval = File(exists=True, mandatory=True)
-    fbvec = File(exists=True, mandatory=True)
-    b0_thr = traits.Int(default_value=50, usedefault=True)
-
-
-class _MakeGtabBmaskOutputSpec(TraitedSpec):
-    """Output interface wrapper for MakeGtabBmask"""
-
-    gtab_file = File(exists=True, mandatory=True)
-    B0_bet = File(exists=True, mandatory=True)
-    B0_mask = File(exists=True, mandatory=True)
-    dwi_file = File(exists=True, mandatory=True)
-
-
-class MakeGtabBmask(SimpleInterface):
-    """Interface wrapper for MakeGtabBmask"""
-
-    input_spec = _MakeGtabBmaskInputSpec
-    output_spec = _MakeGtabBmaskOutputSpec
-
-    def _run_interface(self, runtime):
-        import os
-        import time
-        from dipy.io import save_pickle
-        from dipy.io import read_bvals_bvecs
-        from dipy.core.gradients import gradient_table
-        from nipype.utils.filemanip import copyfile, fname_presuffix
-        # from dipy.segment.mask import median_otsu
-        from pynets.registration.utils import median
-        from pynets.dmri.utils import normalize_gradients, extract_b0
-
-        B0_bet = f"{runtime.cwd}/mean_B0_bet.nii.gz"
-        B0_mask = f"{runtime.cwd}/mean_B0_bet_mask.nii.gz"
-        fbvec_norm = f"{runtime.cwd}/bvec_normed.bvec"
-        fbval_norm = f"{runtime.cwd}/bval_normed.bvec"
-        gtab_file = f"{runtime.cwd}/gtab.pkl"
-        all_b0s_file = f"{runtime.cwd}/all_b0s.nii.gz"
-
-        fbval_tmp_path = fname_presuffix(
-            self.inputs.fbval, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.fbval,
-            fbval_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        fbvec_tmp_path = fname_presuffix(
-            self.inputs.fbvec, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.fbvec,
-            fbvec_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        # loading bvecs/bvals
-        bvals, bvecs = read_bvals_bvecs(fbval_tmp_path, fbvec_tmp_path)
-        bvecs_norm, bvals_norm = normalize_gradients(
-            bvecs, bvals, b0_threshold=self.inputs.b0_thr
-        )
-
-        # Save corrected
-        np.savetxt(fbval_norm, bvals_norm)
-        np.savetxt(fbvec_norm, bvecs_norm)
-
-        # Creating the gradient table
-        gtab = gradient_table(bvals_norm, bvecs_norm)
-
-        # Correct b0 threshold
-        gtab.b0_threshold = self.inputs.b0_thr
-
-        # Correct bvals to set 0's for B0 based on thresh
-        gtab_bvals = gtab.bvals.copy()
-        b0_thr_ixs = np.where(gtab_bvals < gtab.b0_threshold)[0]
-        gtab_bvals[b0_thr_ixs] = 0
-        gtab.b0s_mask = gtab_bvals == 0
-
-        # Show info
-        print(gtab.info)
-
-        # Save gradient table to pickle
-        save_pickle(gtab_file, gtab)
-
-        dwi_file_tmp_path = fname_presuffix(
-            self.inputs.dwi_file, suffix="_tmp", newpath=runtime.cwd
-        )
-        copyfile(
-            self.inputs.dwi_file,
-            dwi_file_tmp_path,
-            copy=True,
-            use_hardlink=False)
-
-        # Extract and Combine all b0s collected, make mean b0
-        print("Extracting b0's...")
-        all_b0s_file = extract_b0(
-            dwi_file_tmp_path,
-            b0_thr_ixs,
-            all_b0s_file)
-        med_b0_file = median(all_b0s_file)
-
-        # TODO replace with bet and median_otsu with deep-learning classifier.
-
-        # med_b0_img = nib.load(med_b0_file)
-        # med_b0_data = np.asarray(med_b0_img.dataobj)
-        # # Create mean b0 brain mask
-        # b0_mask_data, mask_data = median_otsu(med_b0_data, median_radius=2,
-        # numpass=1)
-        #
-        # hdr = med_b0_img.header.copy()
-        # hdr.set_xyzt_units("mm")
-        # hdr.set_data_dtype(np.float32)
-        # nib.Nifti1Image(b0_mask_data, med_b0_img.affine,
-        #                 hdr).to_filename(B0_bet)
-        # nib.Nifti1Image(mask_data, med_b0_img.affine,
-        #                 hdr).to_filename(B0_mask)
-
-        # Get mean B0 brain mask
-        cmd = f"bet {med_b0_file} {B0_bet} -m -f 0.2"
-        os.system(cmd)
-        time.sleep(2)
-
-        self._results["gtab_file"] = gtab_file
-        self._results["B0_bet"] = B0_bet
-        self._results["B0_mask"] = B0_mask
-        self._results["dwi_file"] = self.inputs.dwi_file
-
-        tmp_files = [fbval_tmp_path, fbvec_tmp_path, dwi_file_tmp_path]
-        for j in tmp_files:
-            if j is not None:
-                if os.path.isfile(j):
-                    os.system(f"rm -f {j} &")
-
-        return runtime
diff --git a/pynets/core/nodemaker.py b/pynets/core/nodemaker.py
index 072dacd6..aea4ff15 100644
--- a/pynets/core/nodemaker.py
+++ b/pynets/core/nodemaker.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner
 """
 import warnings
 import numpy as np
@@ -334,12 +333,12 @@ def get_node_membership(
         corresponding to ROI masks.
     perc_overlap : float
         Value 0-1 indicating a threshold of spatial overlap to use as a
-        spatial error cushion in the case of evaluating subnet membership from a
-        given list of parcel masks. Default is 0.75.
+        spatial error cushion in the case of evaluating subnet membership from
+        a given list of parcel masks. Default is 0.75.
     error : int
         Rounded euclidean distance, in units of voxel number, to use as a
-        spatial error cushion in the case of evaluating subnet membership from a
-        given list of coordinates. Default is 4.
+        spatial error cushion in the case of evaluating subnet membership from
+        a given list of coordinates. Default is 4.
 
     Returns
     -------
@@ -709,7 +708,7 @@ def parcel_masker(
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     template_brain = pkg_resources.resource_filename(
@@ -848,11 +847,10 @@ def coords_masker(roi, coords, labels, error, vox_size='2mm'):
         spatial affinity for the specified ROI mask.
     """
     import nibabel as nib
-    from nilearn.image import math_img
+    from nilearn.image import math_img, resample_to_img
     from pynets.core.nodemaker import mmToVox
     import pkg_resources
     import sys
-    from nilearn.image import resample_to_img
     from pynets.core.utils import load_runconfig
 
     mask_img = math_img("img > 0", img=nib.load(roi))
@@ -862,7 +860,7 @@ def coords_masker(roi, coords, labels, error, vox_size='2mm'):
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     template_brain = pkg_resources.resource_filename(
@@ -888,7 +886,6 @@ def coords_masker(roi, coords, labels, error, vox_size='2mm'):
     )
 
     mask_data = mask_img_res.get_fdata().astype('bool')
-
     mask_aff = mask_img_res.affine
 
     x_vox = np.diagonal(mask_aff[:3, 0:3])[0]
@@ -1304,13 +1301,13 @@ def parcel_naming(coords, vox_size):
         labeling_atlases = hardcoded_params["labeling_atlases"]
     except KeyError as e:
         print(e,
-              "No labeling atlases listed in runconfig.yaml"
+              "No labeling atlases listed in advanced.yaml"
               )
     try:
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     template_brain = pkg_resources.resource_filename(
@@ -1413,7 +1410,7 @@ def get_node_attributes(node_files, emb_shape,
                                     atlas='BrainnetomeAtlasFan2016'):
     import ast
     import re
-    from pynets.stats.utils import parse_closest_ixs
+    from pynets.statistics.utils import parse_closest_ixs
 
     ixs, node_dict = parse_closest_ixs(node_files, emb_shape)
 
diff --git a/pynets/core/thresholding.py b/pynets/core/thresholding.py
index 2771e0f0..1e0befec 100644
--- a/pynets/core/thresholding.py
+++ b/pynets/core/thresholding.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import numpy as np
diff --git a/pynets/core/utils.py b/pynets/core/utils.py
index 6b26fd8f..6353bf5b 100644
--- a/pynets/core/utils.py
+++ b/pynets/core/utils.py
@@ -3,7 +3,6 @@
 """
 Created on Fri Nov 10 15:44:46 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import os
@@ -177,7 +176,6 @@ def create_est_path_func(
 
     """
     import os
-    import sys
     from pynets.core.utils import load_runconfig
 
     hardcoded_params = load_runconfig()
@@ -185,7 +183,7 @@ def create_est_path_func(
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     if (node_radius is None) and (parc is True):
@@ -306,7 +304,6 @@ def create_est_path_diff(
 
     """
     import os
-    import sys
     from pynets.core.utils import load_runconfig
 
     hardcoded_params = load_runconfig()
@@ -314,7 +311,7 @@ def create_est_path_diff(
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     if (node_radius is None) and (parc is True):
@@ -421,7 +418,6 @@ def create_raw_path_func(
 
     """
     import os
-    import sys
     from pynets.core.utils import load_runconfig
 
     hardcoded_params = load_runconfig()
@@ -429,7 +425,7 @@ def create_raw_path_func(
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     if (node_radius is None) and (parc is True):
@@ -539,7 +535,6 @@ def create_raw_path_diff(
 
     """
     import os
-    import sys
     from pynets.core.utils import load_runconfig
 
     hardcoded_params = load_runconfig()
@@ -547,7 +542,7 @@ def create_raw_path_diff(
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     if (node_radius is None) and (parc is True):
@@ -684,7 +679,6 @@ def load_mat_ext(
     dens_thresh,
     disp_filt,
 ):
-    from pynets.core.utils import load_mat
 
     conn_matrix = load_mat(est_path)
     return (
@@ -720,9 +714,9 @@ def save_mat(conn_matrix, est_path, fmt=None):
     """
     import numpy as np
     import networkx as nx
+    from pynets.core.utils import load_runconfig
 
     if fmt is None:
-        from pynets.core.utils import load_runconfig
         hardcoded_params = load_runconfig()
         fmt = hardcoded_params["graph_file_format"][0]
 
@@ -796,7 +790,6 @@ def save_mat_thresholded(
     binary,
 ):
     import numpy as np
-    from pynets.core.utils import save_mat
     from nipype.utils.filemanip import fname_presuffix
 
     est_path = fname_presuffix(est_path_orig,
@@ -1256,10 +1249,9 @@ def collect_pandas_df(
         If True, then collect_pandas_df completed successfully.
 
     """
-    import sys
     from pathlib import Path
-    from pynets.core.utils import flatten, load_runconfig
-    from pynets.stats.netstats import collect_pandas_df_make
+    from pynets.statistics.individual.algorithms import collect_pandas_df_make
+    from pynets.core.utils import load_runconfig
 
     # Available functional and structural connectivity models
     hardcoded_params = load_runconfig()
@@ -1268,7 +1260,7 @@ def collect_pandas_df(
     except KeyError as e:
         print(e,
               "available functional models not sucessfully extracted"
-              " from runconfig.yaml"
+              " from advanced.yaml"
               )
     try:
         struct_models = hardcoded_params["available_models"][
@@ -1276,7 +1268,7 @@ def collect_pandas_df(
     except KeyError as e:
         print(e,
               "available structural models not sucessfully extracted"
-              " from runconfig.yaml"
+              " from advanced.yaml"
               )
 
     net_mets_csv_list = list(flatten(net_mets_csv_list))
@@ -1401,7 +1393,7 @@ def check_est_path_existence(est_path_list):
     return est_path_list_ex, bad_ixs
 
 
-def load_runconfig():
+def load_runconfig(location=None):
     import pkg_resources
     import yaml
     import tempfile
@@ -1409,8 +1401,11 @@ def load_runconfig():
     import os
 
     fd, temp_path = tempfile.mkstemp()
-    shutil.copy2(pkg_resources.resource_filename("pynets", "runconfig.yaml"),
-                 temp_path)
+
+    if not location:
+        location = pkg_resources.resource_filename("pynets", "advanced.yaml")
+
+    shutil.copy2(location, temp_path)
     with open(temp_path, mode='r+') as stream:
         hardcoded_params = yaml.load(stream, Loader=yaml.FullLoader)
     stream.close()
@@ -1564,15 +1559,15 @@ def save_nifti_parcels_map(ID, dir_path, subnet, net_parcels_map_nifti,
     import os
     import pkg_resources
     import sys
-    from nilearn.image import resample_to_img
     from pynets.core.utils import load_runconfig
+    from nilearn.image import resample_to_img
 
     hardcoded_params = load_runconfig()
     try:
         template_name = hardcoded_params["template"][0]
     except KeyError as e:
         print(e,
-              "No template specified in runconfig.yaml"
+              "No template specified in advanced.yaml"
               )
 
     namer_dir = f"{dir_path}/parcellations"
@@ -1851,7 +1846,7 @@ def build_args_from_config(modality, arg_dict):
     except KeyError as e:
         print(e,
               "available functional models not successfully extracted"
-              " from runconfig.yaml"
+              " from advanced.yaml"
               )
     try:
         struct_models = hardcoded_params["available_models"][
@@ -1859,7 +1854,7 @@ def build_args_from_config(modality, arg_dict):
     except KeyError as e:
         print(e,
               "available structural models not successfully extracted"
-              " from runconfig.yaml"
+              " from advanced.yaml"
               )
 
     arg_list = []
diff --git a/pynets/core/workflows.py b/pynets/core/workflows.py
index 98492a9d..b02efb53 100644
--- a/pynets/core/workflows.py
+++ b/pynets/core/workflows.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import sys
@@ -17,11 +16,8 @@
 from nipype.pipeline import engine as pe
 from nipype.interfaces import utility as niu
 
-from pathlib import Path
-
 if sys.platform.startswith('win') is False:
-    import indexed_gzip
-# from ..due import due, BibTeX
+    pass
 
 warnings.filterwarnings("ignore")
 
@@ -104,7 +100,7 @@ def workflow_selector(
     into a single-subject workflow"""
     from pynets.core.utils import load_runconfig
     from nipype import Workflow
-    from pynets.stats import spectral
+    from pynets.statistics.individual import spectral
     from pynets.core.utils import pass_meta_ins, pass_meta_outs, \
         pass_meta_ins_multi
 
@@ -139,7 +135,7 @@ def workflow_selector(
     except KeyError as e:
         print(e,
               "available functional models not successfully extracted"
-              " from runconfig.yaml"
+              " from advanced.yaml"
               )
     try:
         struct_models = hardcoded_params["available_models"][
@@ -147,7 +143,7 @@ def workflow_selector(
     except KeyError as e:
         print(e,
               "available structural models not successfully extracted"
-              " from runconfig.yaml"
+              " from advanced.yaml"
               )
 
     # Handle modality logic
@@ -1096,7 +1092,7 @@ def workflow_selector(
                         function=spectral.build_omnetome,
                     ),
                     name="omni_embedding_node_func",
-                    imports=import_list,
+                    imports=import_list
                 )
                 meta_wf.connect(
                     [
@@ -1117,7 +1113,7 @@ def workflow_selector(
                         function=spectral.build_asetomes,
                     ),
                     name="ase_embedding_node_func",
-                    imports=import_list,
+                    imports=import_list
                 )
                 meta_wf.connect(
                     [
@@ -1159,7 +1155,7 @@ def workflow_selector(
                         function=spectral.build_omnetome,
                     ),
                     name="omni_embedding_node_struct",
-                    imports=import_list,
+                    imports=import_list
                 )
                 meta_wf.connect(
                     [
@@ -1180,7 +1176,7 @@ def workflow_selector(
                         function=spectral.build_asetomes,
                     ),
                     name="ase_embedding_node_struct",
-                    imports=import_list,
+                    imports=import_list
                 )
                 meta_wf.connect(
                     [
@@ -1202,7 +1198,7 @@ def workflow_selector(
                 function=spectral.build_masetome,
             ),
             name="mase_embedding_node",
-            imports=import_list,
+            imports=import_list
         )
 
         # Multiplex magic happens in the meta-workflow space.
@@ -1227,6 +1223,7 @@ def workflow_selector(
         )
 
         if float(multiplex) > 0:
+            from pynets.statistics.individual.multiplex import build_multigraphs
             build_multigraphs_node = pe.Node(
                 niu.Function(
                     input_names=["est_path_iterlist", "ID"],
@@ -1240,7 +1237,7 @@ def workflow_selector(
                     function=build_multigraphs,
                 ),
                 name="build_multigraphs_node",
-                imports=import_list,
+                imports=import_list
             )
             meta_wf.connect(
                 [
@@ -1254,7 +1251,7 @@ def workflow_selector(
             )
 
             if plot_switch is True:
-                from pynets.plotting.plot_gen import plot_all_struct_func
+                from pynets.plotting.brain import plot_all_struct_func
 
                 plot_all_struct_func_node = pe.MapNode(
                     niu.Function(
@@ -1275,7 +1272,7 @@ def workflow_selector(
                         "metadata",
                     ],
                     name="plot_all_struct_func_node",
-                    imports=import_list,
+                    imports=import_list
                 )
                 meta_wf.connect(
                     [
@@ -1407,10 +1404,8 @@ def dmri_connectometry(
     """
     A function interface for generating a dMRI connectometry nested workflow
     """
-    from pynets.core import nodemaker, thresholding, utils, interfaces
-    from pynets.registration import register
-    from pynets.registration import utils as regutils
-    from pynets.dmri import estimation
+    from pynets.core import nodemaker, utils, thresholding, interfaces
+    from pynets import dmri, registration
 
     import_list = [
         "import warnings",
@@ -1597,8 +1592,8 @@ def dmri_connectometry(
         niu.Function(
             input_names=["infile", "outdir", "vox_size", "bvecs"],
             output_names=["outfile", "bvecs"],
-            function=regutils.orient_reslice,
-            imports=import_list,
+            function=registration.utils.orient_reslice,
+            imports=import_list
         ),
         name="orient_reslice_dwi_node",
     )
@@ -1614,8 +1609,8 @@ def dmri_connectometry(
         niu.Function(
             input_names=["infile", "outdir", "vox_size"],
             output_names=["outfile"],
-            function=regutils.orient_reslice,
-            imports=import_list,
+            function=registration.utils.orient_reslice,
+            imports=import_list
         ),
         name="orient_reslice_anat_node",
     )
@@ -1632,7 +1627,7 @@ def dmri_connectometry(
                 input_names=["coords", "node_radius", "template_mask"],
                 output_names=["parcel_list", "par_max", "node_radius", "parc"],
                 function=nodemaker.create_spherical_roi_volumes,
-                imports=import_list,
+                imports=import_list
             ),
             name="prep_spherical_nodes_node",
         )
@@ -1655,7 +1650,7 @@ def dmri_connectometry(
                          "vox_size"],
             output_names=["net_parcels_nii_path"],
             function=utils.save_nifti_parcels_map,
-            imports=import_list,
+            imports=import_list
         ),
         name="save_nifti_parcels_node",
     )
@@ -1686,7 +1681,7 @@ def dmri_connectometry(
                     "dir_path",
                 ],
                 function=nodemaker.node_gen_masking,
-                imports=import_list,
+                imports=import_list
             ),
             name="node_gen_node",
         )
@@ -1718,21 +1713,21 @@ def dmri_connectometry(
                     "dir_path",
                 ],
                 function=nodemaker.node_gen,
-                imports=import_list,
+                imports=import_list
             ),
             name="node_gen_node",
         )
     node_gen_node._n_procs = runtime_dict["node_gen_node"][0]
     node_gen_node._mem_gb = runtime_dict["node_gen_node"][1]
 
-    gtab_node = pe.Node(interfaces.MakeGtabBmask(), name="gtab_node")
+    gtab_node = pe.Node(dmri.interfaces.MakeGtabBmask(), name="gtab_node")
 
     get_fa_node = pe.Node(
         niu.Function(
             input_names=["gtab_file", "dwi_file", "B0_mask"],
             output_names=["fa_path", "B0_mask", "gtab_file", "dwi_file"],
-            function=estimation.tens_mod_fa_est,
-            imports=import_list,
+            function=dmri.estimation.tens_mod_fa_est,
+            imports=import_list
         ),
         name="get_fa_node",
     )
@@ -1741,13 +1736,13 @@ def dmri_connectometry(
         niu.Function(
             input_names=["gtab_file", "dwi_file", "B0_mask"],
             output_names=["anisopwr_path", "B0_mask", "gtab_file", "dwi_file"],
-            function=estimation.create_anisopowermap,
-            imports=import_list,
+            function=dmri.estimation.create_anisopowermap,
+            imports=import_list
         ),
         name="get_anisopwr_node",
     )
 
-    register_node = pe.Node(interfaces.RegisterDWI(in_dir=in_dir),
+    register_node = pe.Node(registration.interfaces.RegisterDWI(in_dir=in_dir),
                             name="register_node")
     register_node._n_procs = runtime_dict["register_node"][0]
     register_node._mem_gb = runtime_dict["register_node"][1]
@@ -1757,19 +1752,19 @@ def dmri_connectometry(
         niu.Function(
             input_names=["infile", "outdir", "vox_size"],
             output_names=["outfile"],
-            function=regutils.orient_reslice,
-            imports=import_list,
+            function=registration.utils.orient_reslice,
+            imports=import_list
         ),
         name="orient_reslice_parcellation_node",
     )
 
     reg_nodes_node = pe.Node(
-        interfaces.RegisterAtlasDWI(),
+        registration.interfaces.RegisterAtlasDWI(),
         name="reg_nodes_node")
     reg_nodes_node._n_procs = runtime_dict["reg_nodes_node"][0]
     reg_nodes_node._mem_gb = runtime_dict["reg_nodes_node"][1]
 
-    run_tracking_node = pe.Node(interfaces.Tracking(),
+    run_tracking_node = pe.Node(dmri.interfaces.Tracking(),
                                 name="run_tracking_node")
     run_tracking_node.synchronize = True
     run_tracking_node._n_procs = runtime_dict["run_tracking_node"][0]
@@ -1932,8 +1927,8 @@ def dmri_connectometry(
                 "warped_fa",
                 "min_length",
             ],
-            function=register.direct_streamline_norm,
-            imports=import_list,
+            function=registration.register.direct_streamline_norm,
+            imports=import_list
         ),
         name="dsn_node",
     )
@@ -1995,8 +1990,8 @@ def dmri_connectometry(
                 "min_length",
                 "error_margin"
             ],
-            function=estimation.streams2graph,
-            imports=import_list,
+            function=dmri.estimation.streams2graph,
+            imports=import_list
         ),
         name="streams2graph_node",
     )
@@ -2143,13 +2138,13 @@ def dmri_connectometry(
             niu.Function(
                 input_names=["infile", "outdir", "vox_size"],
                 output_names=["outfile"],
-                function=regutils.orient_reslice,
-                imports=import_list,
+                function=registration.utils.orient_reslice,
+                imports=import_list
             ),
             name="orient_reslice_roi_node",
         )
 
-        register_roi_node = pe.Node(interfaces.RegisterROIDWI(),
+        register_roi_node = pe.Node(registration.interfaces.RegisterROIDWI(),
                                     name="register_roi_node")
         dmri_wf.connect([(inputnode,
                                         orient_reslice_roi_node,
@@ -2168,7 +2163,7 @@ def dmri_connectometry(
         niu.Function(
             input_names=["coords", "labels", "dir_path", "subnet", "indices"],
             function=utils.save_coords_and_labels_to_json,
-            imports=import_list,
+            imports=import_list
         ),
         name="save_coords_and_labels_node",
     )
@@ -2193,7 +2188,7 @@ def dmri_connectometry(
                     "net_labels",
                     "subnet"],
                 function=nodemaker.get_node_membership,
-                imports=import_list,
+                imports=import_list
             ),
             name="get_node_membership_node",
         )
@@ -2775,7 +2770,7 @@ def dmri_connectometry(
                     "error_margin"
                 ],
                 function=thresholding.thresh_struct,
-                imports=import_list,
+                imports=import_list
             ),
             name="thresh_diff_node",
         )
@@ -2809,7 +2804,7 @@ def dmri_connectometry(
                     "error_margin"
                 ],
                 function=thresholding.thresh_struct,
-                imports=import_list,
+                imports=import_list
             ),
             name="thresh_diff_node",
             iterfield=thr_struct_fields,
@@ -2900,6 +2895,7 @@ def dmri_connectometry(
 
     # Plotting
     if plot_switch is True:
+        from pynets import plotting
         plot_fields = [
             "conn_matrix",
             "conn_model",
@@ -2936,13 +2932,13 @@ def dmri_connectometry(
             or flexi_atlas is True
         ):
             plot_all_node = pe.MapNode(
-                interfaces.PlotStruct(),
+                plotting.interfaces.PlotStruct(),
                 iterfield=plot_fields,
                 name="plot_all_node",
                 nested=True)
         else:
             # Plotting singular graph solution
-            plot_all_node = pe.Node(interfaces.PlotStruct(),
+            plot_all_node = pe.Node(plotting.interfaces.PlotStruct(),
                                     name="plot_all_node")
 
         # Connect thresh_diff_node outputs to plotting node
@@ -3260,8 +3256,8 @@ def dmri_connectometry(
             niu.Function(
                 input_names=["infile", "outdir", "vox_size"],
                 output_names=["outfile"],
-                function=regutils.orient_reslice,
-                imports=import_list,
+                function=registration.utils.orient_reslice,
+                imports=import_list
             ),
             name="orient_reslice_waymask_node",
         )
@@ -3302,8 +3298,8 @@ def dmri_connectometry(
             niu.Function(
                 input_names=["infile", "outdir", "vox_size"],
                 output_names=["outfile"],
-                function=regutils.orient_reslice,
-                imports=import_list,
+                function=registration.utils.orient_reslice,
+                imports=import_list
             ),
             name="orient_reslice_mask_node",
         )
@@ -3568,8 +3564,7 @@ def fmri_connectometry(
     A function interface for generating an fMRI connectometry nested workflow
     """
     from pynets.core import nodemaker, utils, thresholding, interfaces
-    from pynets.fmri import estimation
-    from pynets.registration import utils as regutils
+    from pynets import fmri, registration
 
     import_list = [
         "import warnings",
@@ -3764,8 +3759,8 @@ def fmri_connectometry(
         niu.Function(
             input_names=["infile", "outdir", "vox_size"],
             output_names=["outfile"],
-            function=regutils.orient_reslice,
-            imports=import_list,
+            function=registration.utils.orient_reslice,
+            imports=import_list
         ),
         name="orient_reslice_func_node",
     )
@@ -3781,26 +3776,27 @@ def fmri_connectometry(
         niu.Function(
             input_names=["infile", "outdir", "vox_size"],
             output_names=["outfile"],
-            function=regutils.orient_reslice,
-            imports=import_list,
+            function=registration.utils.orient_reslice,
+            imports=import_list
         ),
         name="orient_reslice_anat_node",
     )
 
-    register_node = pe.Node(interfaces.RegisterFunc(in_dir=in_dir),
-                            name="register_node")
+    register_node = pe.Node(
+        registration.interfaces.RegisterFunc(in_dir=in_dir),
+        name="register_node")
 
     register_node._n_procs = runtime_dict["register_node"][0]
     register_node._mem_gb = runtime_dict["register_node"][1]
 
     reg_nodes_node = pe.Node(
-        interfaces.RegisterAtlasFunc(),
+        registration.interfaces.RegisterAtlasFunc(),
         name="reg_nodes_node")
 
     # Clustering
     if float(k_clustering) > 0:
         reg_nodes_node = pe.Node(
-            interfaces.RegisterAtlasFunc(already_run=True),
+            registration.interfaces.RegisterAtlasFunc(already_run=True),
             name="reg_nodes_node")
 
         clustering_info_node = pe.Node(
@@ -3809,7 +3805,7 @@ def fmri_connectometry(
         )
 
         clustering_node = pe.Node(
-            interfaces.IndividualClustering(),
+            fmri.interfaces.IndividualClustering(),
             name="clustering_node")
 
         clustering_node.interface.n_procs = runtime_dict["clustering_node"][0]
@@ -4004,7 +4000,7 @@ def fmri_connectometry(
     if float(k_clustering) > 0:
 
         RegisterParcellation2MNIFunc_node = pe.Node(
-            interfaces.RegisterParcellation2MNIFunc(),
+            registration.interfaces.RegisterParcellation2MNIFunc(),
             name="RegisterParcellation2MNIFunc_node"
         )
 
@@ -4378,7 +4374,7 @@ def fmri_connectometry(
                     "dir_path",
                 ],
                 function=nodemaker.node_gen,
-                imports=import_list,
+                imports=import_list
             ),
             name="node_gen_node",
         )
@@ -4394,7 +4390,7 @@ def fmri_connectometry(
         ),
     )
     extract_ts_node = pe.Node(
-        interfaces.ExtractTimeseries(),
+        fmri.interfaces.ExtractTimeseries(),
         name="extract_ts_node",
     )
 
@@ -4415,7 +4411,7 @@ def fmri_connectometry(
                 input_names=["coords", "node_radius", "template_mask"],
                 output_names=["parcel_list", "par_max", "node_radius", "parc"],
                 function=nodemaker.create_spherical_roi_volumes,
-                imports=import_list,
+                imports=import_list
             ),
             name="prep_spherical_nodes_node",
         )
@@ -4451,7 +4447,7 @@ def fmri_connectometry(
                          "vox_size"],
             output_names=["net_parcels_nii_path"],
             function=utils.save_nifti_parcels_map,
-            imports=import_list,
+            imports=import_list
         ),
         name="save_nifti_parcels_node",
     )
@@ -4604,8 +4600,8 @@ def fmri_connectometry(
                 "hpass",
                 "extract_strategy",
             ],
-            function=estimation.get_conn_matrix,
-            imports=import_list,
+            function=fmri.estimation.get_conn_matrix,
+            imports=import_list
         ),
         name="get_conn_matrix_node",
     )
@@ -4626,13 +4622,13 @@ def fmri_connectometry(
             niu.Function(
                 input_names=["infile", "outdir", "vox_size"],
                 output_names=["outfile"],
-                function=regutils.orient_reslice,
-                imports=import_list,
+                function=registration.utils.orient_reslice,
+                imports=import_list
             ),
             name="orient_reslice_roi_node",
         )
 
-        register_roi_node = pe.Node(interfaces.RegisterROIEPI(),
+        register_roi_node = pe.Node(registration.interfaces.RegisterROIEPI(),
                                     name="register_roi_node")
 
         fmri_wf.connect([(inputnode,
@@ -4654,7 +4650,7 @@ def fmri_connectometry(
         niu.Function(
             input_names=["coords", "labels", "dir_path", "subnet", "indices"],
             function=utils.save_coords_and_labels_to_json,
-            imports=import_list,
+            imports=import_list
         ),
         name="save_coords_and_labels_node",
     )
@@ -4679,7 +4675,7 @@ def fmri_connectometry(
                     "net_labels",
                     "subnet"],
                 function=nodemaker.get_node_membership,
-                imports=import_list,
+                imports=import_list
             ),
             name="get_node_membership_node",
         )
@@ -5129,7 +5125,7 @@ def fmri_connectometry(
                     "extract_strategy",
                 ],
                 function=thresholding.thresh_func,
-                imports=import_list,
+                imports=import_list
             ),
             name="thresh_func_node",
         )
@@ -5159,7 +5155,7 @@ def fmri_connectometry(
                     "extract_strategy",
                 ],
                 function=thresholding.thresh_func,
-                imports=import_list,
+                imports=import_list
             ),
             name="thresh_func_node",
             iterfield=thr_func_fields,
@@ -5242,6 +5238,7 @@ def fmri_connectometry(
 
     # Plotting
     if plot_switch is True:
+        from pynets import plotting
         plot_fields = [
             "conn_matrix",
             "conn_model",
@@ -5279,13 +5276,13 @@ def fmri_connectometry(
         ):
 
             plot_all_node = pe.MapNode(
-                interfaces.PlotFunc(),
+                plotting.interfaces.PlotFunc(),
                 iterfield=plot_fields,
                 name="plot_all_node",
                 nested=True)
         else:
             # Plotting singular graph solution
-            plot_all_node = pe.Node(interfaces.PlotFunc(),
+            plot_all_node = pe.Node(plotting.interfaces.PlotFunc(),
                                     name="plot_all_node")
 
         if user_atlas_list or multi_atlas or multi_nets:
@@ -5447,8 +5444,8 @@ def fmri_connectometry(
             niu.Function(
                 input_names=["infile", "outdir", "vox_size"],
                 output_names=["outfile"],
-                function=regutils.orient_reslice,
-                imports=import_list,
+                function=registration.utils.orient_reslice,
+                imports=import_list
             ),
             name="orient_reslice_mask_node",
         )
@@ -5620,8 +5617,8 @@ def fmri_connectometry(
         niu.Function(
             input_names=["infile", "outdir", "vox_size"],
             output_names=["outfile"],
-            function=regutils.orient_reslice,
-            imports=import_list,
+            function=registration.utils.orient_reslice,
+            imports=import_list
         ),
         name="orient_reslice_parcellation_node",
     )
@@ -5803,7 +5800,7 @@ def raw_graph_workflow(
                 function=thresh_raw_graph,
             ),
             name="thresholding_node",
-            imports=import_list,
+            imports=import_list
         )
 
         thr_info_node = pe.Node(
@@ -5856,7 +5853,7 @@ def raw_graph_workflow(
                 function=save_mat_thresholded,
             ),
             name="save_mat_thresholded_node",
-            imports=import_list,
+            imports=import_list
         )
         save_mat_thresholded_node._n_procs = runtime_dict[
             "save_mat_thresholded_node"][0]
@@ -5961,7 +5958,7 @@ def raw_graph_workflow(
                     function=load_mat_ext,
                 ),
                 name="load_mat_ext_node",
-                imports=import_list,
+                imports=import_list
             )
             load_mat_node._n_procs = runtime_dict["load_mat_ext_node"][0]
             load_mat_node._mem_gb = runtime_dict["load_mat_ext_node"][1]
@@ -6048,7 +6045,7 @@ def raw_graph_workflow(
                     function=load_mat,
                 ),
                 name="load_mat_node",
-                imports=import_list,
+                imports=import_list
             )
             load_mat_node._n_procs = runtime_dict["load_mat_node"][0]
             load_mat_node._mem_gb = runtime_dict["load_mat_node"][1]
diff --git a/pynets/dmri/__init__.py b/pynets/dmri/__init__.py
index 75068549..d61b7bbe 100644
--- a/pynets/dmri/__init__.py
+++ b/pynets/dmri/__init__.py
@@ -4,6 +4,6 @@
 __email__ = "dpisner@utexas.edu"
 
 import warnings
-from . import *
+from . import estimation, interfaces, track, utils
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/dmri/estimation.py b/pynets/dmri/estimation.py
index 8f7045b0..308c5d47 100755
--- a/pynets/dmri/estimation.py
+++ b/pynets/dmri/estimation.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import numpy as np
diff --git a/pynets/dmri/interfaces.py b/pynets/dmri/interfaces.py
new file mode 100755
index 00000000..653bd154
--- /dev/null
+++ b/pynets/dmri/interfaces.py
@@ -0,0 +1,763 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Nov 10 15:44:46 2017
+Copyright (C) 2017
+"""
+import warnings
+import numpy as np
+import sys
+if sys.platform.startswith('win') is False:
+    import indexed_gzip
+import nibabel as nib
+from nipype.interfaces.base import (
+    BaseInterfaceInputSpec,
+    TraitedSpec,
+    File,
+    traits,
+    SimpleInterface,
+    Directory,
+)
+
+warnings.filterwarnings("ignore")
+
+
+class _TrackingInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for Tracking"""
+
+    B0_mask = File(exists=True, mandatory=True)
+    gm_in_dwi = File(exists=True, mandatory=True)
+    vent_csf_in_dwi = File(exists=True, mandatory=True)
+    wm_in_dwi = File(exists=True, mandatory=True)
+    tiss_class = traits.Str(mandatory=True)
+    labels_im_file_wm_gm_int = File(exists=True, mandatory=True)
+    labels_im_file = File(exists=True, mandatory=True)
+    target_samples = traits.Any(mandatory=True)
+    curv_thr_list = traits.List(mandatory=True)
+    step_list = traits.List(mandatory=True)
+    track_type = traits.Str(mandatory=True)
+    min_length = traits.Any(mandatory=True)
+    maxcrossing = traits.Any(mandatory=True)
+    directget = traits.Str(mandatory=True)
+    conn_model = traits.Str(mandatory=True)
+    gtab_file = File(exists=True, mandatory=True)
+    dwi_file = File(exists=True, mandatory=True)
+    subnet = traits.Any(mandatory=False)
+    node_radius = traits.Any(mandatory=True)
+    dens_thresh = traits.Bool(False, mandatory=False)
+    ID = traits.Any(mandatory=True)
+    roi = traits.Any(mandatory=False)
+    min_span_tree = traits.Bool(False, mandatory=False)
+    disp_filt = traits.Bool(False, mandatory=False)
+    parc = traits.Bool(mandatory=True)
+    prune = traits.Any(mandatory=True)
+    atlas = traits.Any(mandatory=False)
+    parcellation = traits.Any(mandatory=False)
+    labels = traits.Any(mandatory=True)
+    coords = traits.Any(mandatory=True)
+    norm = traits.Any(mandatory=True)
+    binary = traits.Bool(False, usedefault=True)
+    atlas_t1w = File(exists=True, mandatory=True)
+    fa_path = File(exists=True, mandatory=True)
+    waymask = traits.Any(mandatory=False)
+    t1w2dwi = File(exists=True, mandatory=True)
+
+
+class _TrackingOutputSpec(TraitedSpec):
+    """Output interface wrapper for Tracking"""
+
+    streams = traits.Any()
+    track_type = traits.Str(mandatory=True)
+    target_samples = traits.Any(mandatory=True)
+    conn_model = traits.Str(mandatory=True)
+    dir_path = Directory(exists=True, mandatory=True)
+    subnet = traits.Any(mandatory=False)
+    node_radius = traits.Any()
+    dens_thresh = traits.Bool(False, mandatory=False)
+    ID = traits.Any(mandatory=True)
+    roi = traits.Any(mandatory=False)
+    min_span_tree = traits.Bool(False, mandatory=False)
+    disp_filt = traits.Bool(False, mandatory=False)
+    parc = traits.Bool()
+    prune = traits.Any()
+    atlas = traits.Any(mandatory=False)
+    parcellation = traits.Any(mandatory=False)
+    labels = traits.Any(mandatory=True)
+    coords = traits.Any(mandatory=True)
+    norm = traits.Any()
+    binary = traits.Bool(False, usedefault=True)
+    atlas_t1w = File(exists=True, mandatory=True)
+    curv_thr_list = traits.List(mandatory=True)
+    step_list = traits.List(mandatory=True)
+    fa_path = File(exists=True, mandatory=True)
+    dm_path = traits.Any()
+    directget = traits.Str(mandatory=True)
+    labels_im_file = File(exists=True, mandatory=True)
+    min_length = traits.Any()
+
+
+class Tracking(SimpleInterface):
+    """Interface wrapper for Tracking"""
+
+    input_spec = _TrackingInputSpec
+    output_spec = _TrackingOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import os
+        import time
+        import os.path as op
+        from dipy.io import load_pickle
+        from colorama import Fore, Style
+        from dipy.data import get_sphere
+        from pynets.core import utils
+        from pynets.core.utils import load_runconfig
+        from pynets.dmri.track import (
+            reconstruction,
+            create_density_map,
+            track_ensemble,
+        )
+        from dipy.io.stateful_tractogram import Space, StatefulTractogram, \
+            Origin
+        from dipy.io.streamline import save_tractogram
+        from nipype.utils.filemanip import copyfile, fname_presuffix
+
+        hardcoded_params = load_runconfig()
+        use_life = hardcoded_params['tracking']["use_life"][0]
+        roi_neighborhood_tol = hardcoded_params['tracking'][
+            "roi_neighborhood_tol"][0]
+        sphere = hardcoded_params['tracking']["sphere"][0]
+
+        dir_path = utils.do_dir_path(
+            self.inputs.atlas, os.path.dirname(self.inputs.dwi_file)
+        )
+
+        namer_dir = "{}/tractography".format(dir_path)
+        if not os.path.isdir(namer_dir):
+            os.makedirs(namer_dir, exist_ok=True)
+
+        # Load diffusion data
+        dwi_file_tmp_path = fname_presuffix(
+            self.inputs.dwi_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.dwi_file,
+            dwi_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        dwi_img = nib.load(dwi_file_tmp_path, mmap=True)
+        dwi_data = dwi_img.get_fdata(dtype=np.float32)
+
+        # Load FA data
+        fa_file_tmp_path = fname_presuffix(
+            self.inputs.fa_path, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.fa_path,
+            fa_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        fa_img = nib.load(fa_file_tmp_path, mmap=True)
+
+        labels_im_file_tmp_path = fname_presuffix(
+            self.inputs.labels_im_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.labels_im_file,
+            labels_im_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        # Load B0 mask
+        B0_mask_tmp_path = fname_presuffix(
+            self.inputs.B0_mask, suffix="_tmp",
+            newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.B0_mask,
+            B0_mask_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        streams = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % (
+            runtime.cwd,
+            "/streamlines_",
+            "%s"
+            % (self.inputs.subnet + "_" if self.inputs.subnet is not None
+               else ""),
+            "%s"
+            % (
+                op.basename(self.inputs.roi).split(".")[0] + "_"
+                if self.inputs.roi is not None
+                else ""
+            ),
+            self.inputs.conn_model,
+            "_",
+            self.inputs.target_samples,
+            "_",
+            "%s"
+            % (
+                "%s%s" % (self.inputs.node_radius, "mm_")
+                if (
+                    (self.inputs.node_radius != "parc")
+                    and (self.inputs.node_radius is not None)
+                )
+                else "parc_"
+            ),
+            "curv-",
+            str(self.inputs.curv_thr_list).replace(", ", "_"),
+            "_step-",
+            str(self.inputs.step_list).replace(", ", "_"),
+            "_directget-",
+            self.inputs.directget,
+            "_minlength-",
+            self.inputs.min_length,
+            ".trk",
+        )
+
+        if os.path.isfile(f"{namer_dir}/{op.basename(streams)}"):
+            from dipy.io.streamline import load_tractogram
+            copyfile(
+                f"{namer_dir}/{op.basename(streams)}",
+                streams,
+                copy=True,
+                use_hardlink=False,
+            )
+            tractogram = load_tractogram(
+                streams,
+                fa_img,
+                bbox_valid_check=False,
+            )
+
+            streamlines = tractogram.streamlines
+
+            # Create streamline density map
+            try:
+                [dir_path, dm_path] = create_density_map(
+                    fa_img,
+                    dir_path,
+                    streamlines,
+                    self.inputs.conn_model,
+                    self.inputs.target_samples,
+                    self.inputs.node_radius,
+                    self.inputs.curv_thr_list,
+                    self.inputs.step_list,
+                    self.inputs.subnet,
+                    self.inputs.roi,
+                    self.inputs.directget,
+                    self.inputs.min_length,
+                    namer_dir,
+                )
+            except BaseException:
+                print('Density map failed. Check tractography output.')
+                dm_path = None
+
+            del streamlines, tractogram
+            fa_img.uncache()
+            dwi_img.uncache()
+            gc.collect()
+            self._results["dm_path"] = dm_path
+            self._results["streams"] = streams
+            recon_path = None
+        else:
+            # Fit diffusion model
+            # Save reconstruction to .npy
+            recon_path = "%s%s%s%s%s%s%s%s" % (
+                runtime.cwd,
+                "/reconstruction_",
+                "%s"
+                % (self.inputs.subnet + "_" if self.inputs.subnet is not None
+                   else ""),
+                "%s"
+                % (
+                    op.basename(self.inputs.roi).split(".")[0] + "_"
+                    if self.inputs.roi is not None
+                    else ""
+                ),
+                self.inputs.conn_model,
+                "_",
+                "%s"
+                % (
+                    "%s%s" % (self.inputs.node_radius, "mm")
+                    if (
+                        (self.inputs.node_radius != "parc")
+                        and (self.inputs.node_radius is not None)
+                    )
+                    else "parc"
+                ),
+                ".hdf5",
+            )
+
+            gtab_file_tmp_path = fname_presuffix(
+                self.inputs.gtab_file, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.gtab_file,
+                gtab_file_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            gtab = load_pickle(gtab_file_tmp_path)
+
+            # Only re-run the reconstruction if we have to
+            if not os.path.isfile(f"{namer_dir}/{op.basename(recon_path)}"):
+                import h5py
+                model, _ = reconstruction(
+                    self.inputs.conn_model,
+                    gtab,
+                    dwi_data,
+                    B0_mask_tmp_path,
+                )
+                with h5py.File(recon_path, 'w') as hf:
+                    hf.create_dataset("reconstruction",
+                                      data=model.astype('float32'), dtype='f4')
+                hf.close()
+
+                copyfile(
+                    recon_path,
+                    f"{namer_dir}/{op.basename(recon_path)}",
+                    copy=True,
+                    use_hardlink=False,
+                )
+                time.sleep(2)
+                del model
+            elif os.path.getsize(f"{namer_dir}/{op.basename(recon_path)}") > 0:
+                print(
+                    f"Found existing reconstruction with "
+                    f"{self.inputs.conn_model}. Loading...")
+                copyfile(
+                    f"{namer_dir}/{op.basename(recon_path)}",
+                    recon_path,
+                    copy=True,
+                    use_hardlink=False,
+                )
+                time.sleep(5)
+            else:
+                import h5py
+                model, _ = reconstruction(
+                    self.inputs.conn_model,
+                    gtab,
+                    dwi_data,
+                    B0_mask_tmp_path,
+                )
+                with h5py.File(recon_path, 'w') as hf:
+                    hf.create_dataset("reconstruction",
+                                      data=model.astype('float32'), dtype='f4')
+                hf.close()
+
+                copyfile(
+                    recon_path,
+                    f"{namer_dir}/{op.basename(recon_path)}",
+                    copy=True,
+                    use_hardlink=False,
+                )
+                time.sleep(5)
+                del model
+            dwi_img.uncache()
+            del dwi_data
+
+            # Load atlas wm-gm interface reduced version for seeding
+            labels_im_file_tmp_path_wm_gm_int = fname_presuffix(
+                self.inputs.labels_im_file_wm_gm_int, suffix="_tmp",
+                newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.labels_im_file_wm_gm_int,
+                labels_im_file_tmp_path_wm_gm_int,
+                copy=True,
+                use_hardlink=False)
+
+            t1w2dwi_tmp_path = fname_presuffix(
+                self.inputs.t1w2dwi, suffix="_tmp",
+                newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.t1w2dwi,
+                t1w2dwi_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            gm_in_dwi_tmp_path = fname_presuffix(
+                self.inputs.gm_in_dwi, suffix="_tmp",
+                newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.gm_in_dwi,
+                gm_in_dwi_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            vent_csf_in_dwi_tmp_path = fname_presuffix(
+                self.inputs.vent_csf_in_dwi, suffix="_tmp",
+                newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.vent_csf_in_dwi,
+                vent_csf_in_dwi_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            wm_in_dwi_tmp_path = fname_presuffix(
+                self.inputs.wm_in_dwi, suffix="_tmp",
+                newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.wm_in_dwi,
+                wm_in_dwi_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            if self.inputs.waymask:
+                waymask_tmp_path = fname_presuffix(
+                    self.inputs.waymask, suffix="_tmp",
+                    newpath=runtime.cwd
+                )
+                copyfile(
+                    self.inputs.waymask,
+                    waymask_tmp_path,
+                    copy=True,
+                    use_hardlink=False)
+            else:
+                waymask_tmp_path = None
+
+            # Iteratively build a list of streamlines for each ROI while
+            # tracking
+            print(
+                f"{Fore.GREEN}Target streamlines per iteration: "
+                f"{Fore.BLUE} "
+                f"{self.inputs.target_samples}"
+            )
+            print(Style.RESET_ALL)
+            print(
+                f"{Fore.GREEN}Curvature threshold(s): {Fore.BLUE} "
+                f"{self.inputs.curv_thr_list}"
+            )
+            print(Style.RESET_ALL)
+            print(f"{Fore.GREEN}Step size(s): {Fore.BLUE} "
+                  f"{self.inputs.step_list}")
+            print(Style.RESET_ALL)
+            print(f"{Fore.GREEN}Tracking type: {Fore.BLUE} "
+                  f"{self.inputs.track_type}")
+            print(Style.RESET_ALL)
+            if self.inputs.directget == "prob":
+                print(f"{Fore.GREEN}Direction-getting type: {Fore.BLUE}"
+                      f"Probabilistic")
+            elif self.inputs.directget == "clos":
+                print(f"{Fore.GREEN}Direction-getting type: "
+                      f"{Fore.BLUE}Closest Peak")
+            elif self.inputs.directget == "det":
+                print(
+                    f"{Fore.GREEN}Direction-getting type: "
+                    f"{Fore.BLUE}Deterministic Maximum"
+                )
+            else:
+                raise ValueError("Direction-getting type not recognized!")
+
+            print(Style.RESET_ALL)
+
+            # Commence Ensemble Tractography
+            try:
+                streamlines = track_ensemble(
+                    self.inputs.target_samples,
+                    labels_im_file_tmp_path_wm_gm_int,
+                    labels_im_file_tmp_path,
+                    recon_path,
+                    get_sphere(sphere),
+                    self.inputs.directget,
+                    self.inputs.curv_thr_list,
+                    self.inputs.step_list,
+                    self.inputs.track_type,
+                    self.inputs.maxcrossing,
+                    int(roi_neighborhood_tol),
+                    self.inputs.min_length,
+                    waymask_tmp_path,
+                    B0_mask_tmp_path,
+                    t1w2dwi_tmp_path, gm_in_dwi_tmp_path,
+                    vent_csf_in_dwi_tmp_path, wm_in_dwi_tmp_path,
+                    self.inputs.tiss_class
+                )
+                gc.collect()
+            except BaseException as w:
+                print(f"\n{Fore.RED}Tractography failed: {w}")
+                print(Style.RESET_ALL)
+                streamlines = None
+
+            if streamlines is not None:
+                # import multiprocessing
+                # from pynets.core.utils import kill_process_family
+                # return kill_process_family(int(
+                # multiprocessing.current_process().pid))
+
+                # Linear Fascicle Evaluation (LiFE)
+                if use_life is True:
+                    print('Using LiFE to evaluate streamline plausibility...')
+                    from pynets.dmri.utils import \
+                        evaluate_streamline_plausibility
+                    dwi_img = nib.load(dwi_file_tmp_path)
+                    dwi_data = dwi_img.get_fdata(dtype=np.float32)
+                    orig_count = len(streamlines)
+
+                    if self.inputs.waymask:
+                        mask_data = nib.load(
+                            waymask_tmp_path).get_fdata().astype(
+                            'bool').astype('int')
+                    else:
+                        mask_data = nib.load(
+                            wm_in_dwi_tmp_path).get_fdata().astype(
+                            'bool').astype('int')
+                    try:
+                        streamlines = evaluate_streamline_plausibility(
+                            dwi_data, gtab, mask_data, streamlines,
+                            sphere=sphere)
+                    except BaseException:
+                        print(f"Linear Fascicle Evaluation failed. "
+                              f"Visually checking streamlines output "
+                              f"{namer_dir}/{op.basename(streams)} is "
+                              f"recommended.")
+                    if len(streamlines) < 0.5*orig_count:
+                        raise ValueError('LiFE revealed no plausible '
+                                         'streamlines in the tractogram!')
+                    del dwi_data, mask_data
+
+                # Save streamlines to trk
+                stf = StatefulTractogram(
+                    streamlines,
+                    fa_img,
+                    origin=Origin.NIFTI,
+                    space=Space.VOXMM)
+                stf.remove_invalid_streamlines()
+
+                save_tractogram(
+                    stf,
+                    streams,
+                )
+
+                del stf
+
+                copyfile(
+                    streams,
+                    f"{namer_dir}/{op.basename(streams)}",
+                    copy=True,
+                    use_hardlink=False,
+                )
+
+                # Create streamline density map
+                try:
+                    [dir_path, dm_path] = create_density_map(
+                        dwi_img,
+                        dir_path,
+                        streamlines,
+                        self.inputs.conn_model,
+                        self.inputs.target_samples,
+                        self.inputs.node_radius,
+                        self.inputs.curv_thr_list,
+                        self.inputs.step_list,
+                        self.inputs.subnet,
+                        self.inputs.roi,
+                        self.inputs.directget,
+                        self.inputs.min_length,
+                        namer_dir,
+                    )
+                except BaseException:
+                    print('Density map failed. Check tractography output.')
+                    dm_path = None
+
+                del streamlines
+                dwi_img.uncache()
+                gc.collect()
+                self._results["dm_path"] = dm_path
+                self._results["streams"] = streams
+            else:
+                self._results["streams"] = None
+                self._results["dm_path"] = None
+            tmp_files = [gtab_file_tmp_path,
+                         wm_in_dwi_tmp_path, gm_in_dwi_tmp_path,
+                         vent_csf_in_dwi_tmp_path, t1w2dwi_tmp_path]
+
+            for j in tmp_files:
+                if j is not None:
+                    if os.path.isfile(j):
+                        os.system(f"rm -f {j} &")
+
+        self._results["track_type"] = self.inputs.track_type
+        self._results["target_samples"] = self.inputs.target_samples
+        self._results["conn_model"] = self.inputs.conn_model
+        self._results["dir_path"] = dir_path
+        self._results["subnet"] = self.inputs.subnet
+        self._results["node_radius"] = self.inputs.node_radius
+        self._results["dens_thresh"] = self.inputs.dens_thresh
+        self._results["ID"] = self.inputs.ID
+        self._results["roi"] = self.inputs.roi
+        self._results["min_span_tree"] = self.inputs.min_span_tree
+        self._results["disp_filt"] = self.inputs.disp_filt
+        self._results["parc"] = self.inputs.parc
+        self._results["prune"] = self.inputs.prune
+        self._results["atlas"] = self.inputs.atlas
+        self._results["parcellation"] = self.inputs.parcellation
+        self._results["labels"] = self.inputs.labels
+        self._results["coords"] = self.inputs.coords
+        self._results["norm"] = self.inputs.norm
+        self._results["binary"] = self.inputs.binary
+        self._results["atlas_t1w"] = self.inputs.atlas_t1w
+        self._results["curv_thr_list"] = self.inputs.curv_thr_list
+        self._results["step_list"] = self.inputs.step_list
+        self._results["fa_path"] = fa_file_tmp_path
+        self._results["directget"] = self.inputs.directget
+        self._results["labels_im_file"] = labels_im_file_tmp_path
+        self._results["min_length"] = self.inputs.min_length
+
+        tmp_files = [B0_mask_tmp_path, dwi_file_tmp_path]
+
+        for j in tmp_files:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        # Exercise caution when deleting copied recon_path
+        # if recon_path is not None:
+        #     if os.path.isfile(recon_path):
+        #         os.remove(recon_path)
+
+        return runtime
+
+
+class _MakeGtabBmaskInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for MakeGtabBmask"""
+
+    dwi_file = File(exists=True, mandatory=True)
+    fbval = File(exists=True, mandatory=True)
+    fbvec = File(exists=True, mandatory=True)
+    b0_thr = traits.Int(default_value=50, usedefault=True)
+
+
+class _MakeGtabBmaskOutputSpec(TraitedSpec):
+    """Output interface wrapper for MakeGtabBmask"""
+
+    gtab_file = File(exists=True, mandatory=True)
+    B0_bet = File(exists=True, mandatory=True)
+    B0_mask = File(exists=True, mandatory=True)
+    dwi_file = File(exists=True, mandatory=True)
+
+
+class MakeGtabBmask(SimpleInterface):
+    """Interface wrapper for MakeGtabBmask"""
+
+    input_spec = _MakeGtabBmaskInputSpec
+    output_spec = _MakeGtabBmaskOutputSpec
+
+    def _run_interface(self, runtime):
+        import os
+        import time
+        from dipy.io import save_pickle
+        from dipy.io import read_bvals_bvecs
+        from dipy.core.gradients import gradient_table
+        from nipype.utils.filemanip import copyfile, fname_presuffix
+        # from dipy.segment.mask import median_otsu
+        from pynets.registration.utils import median
+        from pynets.dmri.utils import normalize_gradients, extract_b0
+
+        B0_bet = f"{runtime.cwd}/mean_B0_bet.nii.gz"
+        B0_mask = f"{runtime.cwd}/mean_B0_bet_mask.nii.gz"
+        fbvec_norm = f"{runtime.cwd}/bvec_normed.bvec"
+        fbval_norm = f"{runtime.cwd}/bval_normed.bvec"
+        gtab_file = f"{runtime.cwd}/gtab.pkl"
+        all_b0s_file = f"{runtime.cwd}/all_b0s.nii.gz"
+
+        fbval_tmp_path = fname_presuffix(
+            self.inputs.fbval, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.fbval,
+            fbval_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        fbvec_tmp_path = fname_presuffix(
+            self.inputs.fbvec, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.fbvec,
+            fbvec_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        # loading bvecs/bvals
+        bvals, bvecs = read_bvals_bvecs(fbval_tmp_path, fbvec_tmp_path)
+        bvecs_norm, bvals_norm = normalize_gradients(
+            bvecs, bvals, b0_threshold=self.inputs.b0_thr
+        )
+
+        # Save corrected
+        np.savetxt(fbval_norm, bvals_norm)
+        np.savetxt(fbvec_norm, bvecs_norm)
+
+        # Creating the gradient table
+        gtab = gradient_table(bvals_norm, bvecs_norm)
+
+        # Correct b0 threshold
+        gtab.b0_threshold = self.inputs.b0_thr
+
+        # Correct bvals to set 0's for B0 based on thresh
+        gtab_bvals = gtab.bvals.copy()
+        b0_thr_ixs = np.where(gtab_bvals < gtab.b0_threshold)[0]
+        gtab_bvals[b0_thr_ixs] = 0
+        gtab.b0s_mask = gtab_bvals == 0
+
+        # Show info
+        print(gtab.info)
+
+        # Save gradient table to pickle
+        save_pickle(gtab_file, gtab)
+
+        dwi_file_tmp_path = fname_presuffix(
+            self.inputs.dwi_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.dwi_file,
+            dwi_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        # Extract and Combine all b0s collected, make mean b0
+        print("Extracting b0's...")
+        all_b0s_file = extract_b0(
+            dwi_file_tmp_path,
+            b0_thr_ixs,
+            all_b0s_file)
+        med_b0_file = median(all_b0s_file)
+
+        # TODO replace with bet and median_otsu with deep-learning classifier.
+
+        # med_b0_img = nib.load(med_b0_file)
+        # med_b0_data = np.asarray(med_b0_img.dataobj)
+        # # Create mean b0 brain mask
+        # b0_mask_data, mask_data = median_otsu(med_b0_data, median_radius=2,
+        # numpass=1)
+        #
+        # hdr = med_b0_img.header.copy()
+        # hdr.set_xyzt_units("mm")
+        # hdr.set_data_dtype(np.float32)
+        # nib.Nifti1Image(b0_mask_data, med_b0_img.affine,
+        #                 hdr).to_filename(B0_bet)
+        # nib.Nifti1Image(mask_data, med_b0_img.affine,
+        #                 hdr).to_filename(B0_mask)
+
+        # Get mean B0 brain mask
+        cmd = f"bet {med_b0_file} {B0_bet} -m -f 0.2"
+        os.system(cmd)
+        time.sleep(2)
+
+        self._results["gtab_file"] = gtab_file
+        self._results["B0_bet"] = B0_bet
+        self._results["B0_mask"] = B0_mask
+        self._results["dwi_file"] = self.inputs.dwi_file
+
+        tmp_files = [fbval_tmp_path, fbvec_tmp_path, dwi_file_tmp_path]
+        for j in tmp_files:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        return runtime
diff --git a/pynets/dmri/track.py b/pynets/dmri/track.py
index f7c2caa5..c19c37ba 100644
--- a/pynets/dmri/track.py
+++ b/pynets/dmri/track.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import numpy as np
@@ -29,6 +28,7 @@ def reconstruction(conn_model, gtab, dwi_data, B0_mask):
         4D array of dwi data.
     B0_mask : str
         File path to B0 brain mask.
+
     Returns
     -------
     mod_fit : ndarray
@@ -92,6 +92,7 @@ def prep_tissues(
         Tissue classification method.
     cmc_step_size : float
         Step size from CMC tissue classification method.
+
     Returns
     -------
     tiss_classifier : obj
@@ -238,6 +239,7 @@ def create_density_map(
         closest (clos), boot (bootstrapped), and prob (probabilistic).
     min_length : int
         Minimum fiber length threshold in mm to restrict tracking.
+
     Returns
     -------
     streams : str
@@ -387,6 +389,7 @@ def track_ensemble(
         Number of particles to use in the particle filter.
     min_separation_angle : float
         The minimum angle between directions [0, 90].
+
     Returns
     -------
     streamlines : ArraySequence
@@ -619,7 +622,6 @@ def run_tracking(step_curv_combinations, recon_shelved,
     )
     from nilearn.image import index_img
     from pynets.dmri.utils import generate_seeds, random_seeds_from_mask
-    from pynets.dmri.track import prep_tissues
     from nibabel.streamlines.array_sequence import ArraySequence
     from nilearn.image import math_img
 
diff --git a/pynets/dmri/utils.py b/pynets/dmri/utils.py
index fd22fa84..30d296d1 100644
--- a/pynets/dmri/utils.py
+++ b/pynets/dmri/utils.py
@@ -3,10 +3,8 @@
 """
 Created on Fri Nov 10 15:44:46 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
-import os
 import sys
 if sys.platform.startswith('win') is False:
     import indexed_gzip
@@ -125,9 +123,6 @@ def random_seeds_from_mask(mask, seeds_count, affine=np.eye(4), random_seed=1):
         The number of seeds to generate. If ``seed_count_per_voxel`` is True,
         specifies the number of seeds to place in each voxel. Otherwise,
         specifies the total number of seeds to place in the mask.
-    seed_count_per_voxel: bool
-        If True, seeds_count is per voxel, else seeds_count is the total number
-        of seeds.
     random_seed : int
         The seed for the random seed generator (numpy.random.seed).
 
@@ -139,9 +134,6 @@ def random_seeds_from_mask(mask, seeds_count, affine=np.eye(4), random_seed=1):
     ValueError
         When ``mask`` is not a three-dimensional array
     """
-    from scipy.sparse import csc_matrix
-    from scipy import prod
-    from nilearn.image import math_img
 
     mask = np.array(mask, dtype=bool, copy=False, ndmin=3)
     if mask.ndim != 3:
diff --git a/pynets/due.py b/pynets/due.py
index 69df22a5..f729f843 100644
--- a/pynets/due.py
+++ b/pynets/due.py
@@ -1,41 +1,49 @@
+# emacs: at the end of the file
+# ex: set sts=4 ts=4 sw=4 et:
+# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### #
 """
+
 Stub file for a guaranteed safe import of duecredit constructs:  if duecredit
 is not available.
+
 To use it, place it into your project codebase to be imported, e.g. copy as
+
     cp stub.py /path/tomodule/module/due.py
+
 Note that it might be better to avoid naming it duecredit.py to avoid shadowing
 installed duecredit.
+
 Then use in your code as
+
     from .due import due, Doi, BibTeX, Text
+
 See  https://github.com/duecredit/duecredit/blob/master/README.md for examples.
+
 Origin:     Originally a part of the duecredit
-Copyright:  2015-2019  DueCredit developers
+Copyright:  2015-2021  DueCredit developers
 License:    BSD-2
 """
 
-__version__ = "0.0.8"
+__version__ = '0.0.9'
 
 
 class InactiveDueCreditCollector(object):
     """Just a stub at the Collector which would not do anything"""
-
     def _donothing(self, *args, **kwargs):
         """Perform no good and no bad"""
         pass
 
     def dcite(self, *args, **kwargs):
         """If I could cite I would"""
-
         def nondecorating_decorator(func):
             return func
-
         return nondecorating_decorator
 
     active = False
     activate = add = cite = dump = load = _donothing
 
     def __repr__(self):
-        return self.__class__.__name__ + "()"
+        return self.__class__.__name__ + '()'
 
 
 def _donothing_func(*args, **kwargs):
@@ -44,18 +52,23 @@ def _donothing_func(*args, **kwargs):
 
 
 try:
-    from duecredit import due, BibTeX, Doi, Url, Text
-
-    if "due" in locals() and not hasattr(due, "cite"):
+    from duecredit import due, BibTeX, Doi, Url, Text  # lgtm [py/unused-import]
+    if 'due' in locals() and not hasattr(due, 'cite'):
         raise RuntimeError(
             "Imported due lacks .cite. DueCredit is now disabled")
 except Exception as e:
     if not isinstance(e, ImportError):
         import logging
-
         logging.getLogger("duecredit").error(
-            "Failed to import duecredit due to %s" % str(e)
-        )
+            "Failed to import duecredit due to %s" % str(e))
     # Initiate due stub
     due = InactiveDueCreditCollector()
     BibTeX = Doi = Url = Text = _donothing_func
+
+# Emacs mode definitions
+# Local Variables:
+# mode: python
+# py-indent-offset: 4
+# tab-width: 4
+# indent-tabs-mode: nil
+# End:
diff --git a/pynets/fmri/__init__.py b/pynets/fmri/__init__.py
index 75068549..a720cab0 100644
--- a/pynets/fmri/__init__.py
+++ b/pynets/fmri/__init__.py
@@ -4,6 +4,6 @@
 __email__ = "dpisner@utexas.edu"
 
 import warnings
-from . import *
+from . import clustering, estimation, interfaces
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/fmri/clustools.py b/pynets/fmri/clustering.py
similarity index 97%
rename from pynets/fmri/clustools.py
rename to pynets/fmri/clustering.py
index ce619947..fda8dce8 100644
--- a/pynets/fmri/clustools.py
+++ b/pynets/fmri/clustering.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import sys
 if sys.platform.startswith('win') is False:
@@ -36,11 +35,10 @@ def indx_1dto3d(idx, sz):
     z : int
         z-coordinate of 3D matrix coordinates.
     """
-    from scipy import divide, prod
 
-    x = divide(idx, prod(sz[1:3]))
-    y = divide(idx - x * prod(sz[1:3]), sz[2])
-    z = idx - x * prod(sz[1:3]) - y * sz[2]
+    x = np.divide(idx, np.prod(sz[1:3]))
+    y = np.divide(idx - x * np.prod(sz[1:3]), sz[2])
+    z = idx - x * np.prod(sz[1:3]) - y * sz[2]
     return x, y, z
 
 
@@ -61,12 +59,11 @@ def indx_3dto1d(idx, sz):
     idx1 : array
         A 1D numpy coordinate vector.
     """
-    from scipy import prod
 
     if np.linalg.matrix_rank(idx) == 1:
-        idx1 = idx[0] * prod(sz[1:3]) + idx[1] * sz[2] + idx[2]
+        idx1 = idx[0] * np.prod(sz[1:3]) + idx[1] * sz[2] + idx[2]
     else:
-        idx1 = idx[:, 0] * prod(sz[1:3]) + idx[:, 1] * sz[2] + idx[:, 2]
+        idx1 = idx[:, 0] * np.prod(sz[1:3]) + idx[:, 1] * sz[2] + idx[:, 2]
     return idx1
 
 
@@ -384,9 +381,7 @@ def make_local_connectivity_scorr(func_img, clust_mask_img, thresh):
 
     """
     from scipy.sparse import csc_matrix
-    from scipy import prod
     from itertools import product
-    from pynets.fmri.clustools import indx_1dto3d, indx_3dto1d
 
     neighbors = np.array(
         sorted(
@@ -409,7 +404,7 @@ def make_local_connectivity_scorr(func_img, clust_mask_img, thresh):
     mskdat = np.reshape(
         np.asarray(
             clust_mask_img.dataobj).astype("bool"),
-        prod(msz))
+        np.prod(msz))
 
     # Determine the 1D coordinates of the non-zero
     # elements of the mask
@@ -418,7 +413,7 @@ def make_local_connectivity_scorr(func_img, clust_mask_img, thresh):
 
     # Reshape fmri data to a num_voxels x num_timepoints array
     func_data = func_img.get_fdata(dtype=np.float32)
-    imdat = np.reshape(func_data, (prod(sz[:3]), sz[3]))
+    imdat = np.reshape(func_data, (np.prod(sz[:3]), sz[3]))
     del func_data
 
     # Mask the datset to only the in-mask voxels
@@ -448,7 +443,8 @@ def make_local_connectivity_scorr(func_img, clust_mask_img, thresh):
 
     # Construct a sparse matrix from the mask
     msk = csc_matrix(
-        (vndx + 1, (iv, np.zeros(m))), shape=(prod(msz), 1), dtype=np.float32
+        (vndx + 1, (iv, np.zeros(m))), shape=(np.prod(msz), 1),
+        dtype=np.float32
     )
 
     sparse_i = []
@@ -506,7 +502,7 @@ def make_local_connectivity_scorr(func_img, clust_mask_img, thresh):
         sparse_w = np.append(sparse_w, R[nndx, :], 1)
 
     # Ensure that the weight vector is the correct shape
-    sparse_w = np.reshape(sparse_w, prod(np.shape(sparse_w)))
+    sparse_w = np.reshape(sparse_w, np.prod(np.shape(sparse_w)))
 
     # Concatenate the i, j, and w_ij vectors
     outlist = sparse_i
@@ -566,9 +562,7 @@ def make_local_connectivity_tcorr(func_img, clust_mask_img, thresh):
 
     """
     from scipy.sparse import csc_matrix
-    from scipy import prod
     from itertools import product
-    from pynets.fmri.clustools import indx_1dto3d, indx_3dto1d
 
     # Index array used to calculate 3D neigbors
     neighbors = np.array(
@@ -592,7 +586,7 @@ def make_local_connectivity_tcorr(func_img, clust_mask_img, thresh):
     mskdat = np.reshape(
         np.asarray(
             clust_mask_img.dataobj).astype("bool"),
-        prod(msz))
+        np.prod(msz))
 
     # Determine the 1D coordinates of the non-zero elements of the mask
     iv = np.nonzero(mskdat)[0]
@@ -602,13 +596,13 @@ def make_local_connectivity_tcorr(func_img, clust_mask_img, thresh):
 
     # Reshape fmri data to a num_voxels x num_timepoints array
     func_data = func_img.get_fdata(dtype=np.float32)
-    imdat = np.reshape(func_data, (prod(sz[:3]), sz[3]))
+    imdat = np.reshape(func_data, (np.prod(sz[:3]), sz[3]))
     del func_data
 
     # Construct a sparse matrix from the mask
     msk = csc_matrix(
         (list(range(1, m + 1)), (iv, np.zeros(m))),
-        shape=(prod(sz[:-1]), 1),
+        shape=(np.prod(sz[:-1]), 1),
         dtype=np.float32,
     )
     sparse_i = []
@@ -947,7 +941,7 @@ def create_local_clustering(self, overwrite, r_thresh, min_region_size=80):
 
                 if (not op.isfile(self._local_conn_mat_path)) or (
                         overwrite is True):
-                    from pynets.fmri.clustools import (
+                    from pynets.fmri.clustering import (
                         make_local_connectivity_tcorr,
                         make_local_connectivity_scorr,
                     )
diff --git a/pynets/fmri/estimation.py b/pynets/fmri/estimation.py
index ca014970..ffddb9b2 100644
--- a/pynets/fmri/estimation.py
+++ b/pynets/fmri/estimation.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import numpy as np
@@ -555,7 +554,7 @@ def __init__(
         except KeyError as e:
             print(e,
                   "ERROR: Plotting configuration not successfully "
-                  "extracted from runconfig.yaml"
+                  "extracted from advanced.yaml"
                   )
 
     def prepare_inputs(self, num_std_dev=1.5):
diff --git a/pynets/fmri/interfaces.py b/pynets/fmri/interfaces.py
new file mode 100755
index 00000000..5580e974
--- /dev/null
+++ b/pynets/fmri/interfaces.py
@@ -0,0 +1,508 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Nov 10 15:44:46 2017
+Copyright (C) 2017
+"""
+import warnings
+import sys
+if sys.platform.startswith('win') is False:
+    import indexed_gzip
+from nipype.interfaces.base import (
+    BaseInterfaceInputSpec,
+    TraitedSpec,
+    File,
+    traits,
+    SimpleInterface,
+    Directory,
+)
+
+warnings.filterwarnings("ignore")
+
+
+class _IndividualClusteringInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for IndividualClustering"""
+
+    func_file = File(exists=True, mandatory=True)
+    conf = traits.Any(mandatory=False)
+    clust_mask = File(exists=True, mandatory=True)
+    ID = traits.Any(mandatory=True)
+    k = traits.Any(mandatory=True)
+    clust_type = traits.Str(mandatory=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    local_corr = traits.Str("allcorr", mandatory=True, usedefault=True)
+    mask = traits.Any(mandatory=False)
+    outdir = traits.Str(mandatory=True)
+    basedir_path = Directory(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    simple = traits.Bool(False, usedefault=True)
+
+
+class _IndividualClusteringOutputSpec(TraitedSpec):
+    """Output interface wrapper for IndividualClustering"""
+
+    parcellation = File(exists=True)
+    atlas = traits.Str(mandatory=True)
+    clustering = traits.Bool(True, usedefault=True)
+    clust_mask = File(exists=True, mandatory=True)
+    k = traits.Any(mandatory=True)
+    clust_type = traits.Str(mandatory=True)
+    func_file = File(exists=True, mandatory=True)
+
+
+class IndividualClustering(SimpleInterface):
+    """Interface wrapper for IndividualClustering"""
+
+    input_spec = _IndividualClusteringInputSpec
+    output_spec = _IndividualClusteringOutputSpec
+
+    def _run_interface(self, runtime):
+        import os
+        import gc
+        import time
+        import nibabel as nib
+        from pynets.core.utils import load_runconfig
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        from pynets.fmri import clustering
+        from pynets.registration.utils import orient_reslice
+        from joblib import Parallel, delayed
+        from joblib.externals.loky.backend import resource_tracker
+        from pynets.registration import utils as regutils
+        from pynets.core.utils import decompress_nifti
+        import pkg_resources
+        import shutil
+        import tempfile
+        resource_tracker.warnings = None
+
+        template = pkg_resources.resource_filename(
+            "pynets", f"templates/{self.inputs.template_name}_brain_"
+                      f"{self.inputs.vox_size}.nii.gz"
+        )
+
+        template_tmp_path = fname_presuffix(
+            template, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            template,
+            template_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        hardcoded_params = load_runconfig()
+        c_boot = hardcoded_params["c_boot"][0]
+        nthreads = hardcoded_params["omp_threads"][0]
+
+        clust_list = ["kmeans", "ward", "complete", "average", "ncut", "rena"]
+
+        clust_mask_temp_path = orient_reslice(
+            self.inputs.clust_mask, runtime.cwd, self.inputs.vox_size
+        )
+        cm_suf = os.path.basename(self.inputs.clust_mask).split('.nii')[0]
+        clust_mask_in_t1w_path = f"{runtime.cwd}/clust_mask-" \
+                                 f"{cm_suf}_in_t1w.nii.gz"
+
+        t1w_brain_tmp_path = fname_presuffix(
+            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w_brain,
+            t1w_brain_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        mni2t1w_warp_tmp_path = fname_presuffix(
+            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1w_warp,
+            mni2t1w_warp_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        mni2t1_xfm_tmp_path = fname_presuffix(
+            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1_xfm,
+            mni2t1_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        clust_mask_in_t1w = regutils.roi2t1w_align(
+            clust_mask_temp_path,
+            t1w_brain_tmp_path,
+            mni2t1_xfm_tmp_path,
+            mni2t1w_warp_tmp_path,
+            clust_mask_in_t1w_path,
+            template_tmp_path,
+            self.inputs.simple,
+        )
+        time.sleep(0.5)
+
+        if self.inputs.mask:
+            out_name_mask = fname_presuffix(
+                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mask,
+                out_name_mask,
+                copy=True,
+                use_hardlink=False)
+        else:
+            out_name_mask = None
+
+        out_name_func_file = fname_presuffix(
+            self.inputs.func_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.func_file,
+            out_name_func_file,
+            copy=True,
+            use_hardlink=False)
+        out_name_func_file = decompress_nifti(out_name_func_file)
+
+        if self.inputs.conf:
+            out_name_conf = fname_presuffix(
+                self.inputs.conf, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.conf,
+                out_name_conf,
+                copy=True,
+                use_hardlink=False)
+        else:
+            out_name_conf = None
+
+        nip = clustering.NiParcellate(
+            func_file=out_name_func_file,
+            clust_mask=clust_mask_in_t1w,
+            k=int(self.inputs.k),
+            clust_type=self.inputs.clust_type,
+            local_corr=self.inputs.local_corr,
+            outdir=self.inputs.outdir,
+            conf=out_name_conf,
+            mask=out_name_mask,
+        )
+
+        atlas = nip.create_clean_mask()
+        nip.create_local_clustering(overwrite=True, r_thresh=0.4)
+
+        if self.inputs.clust_type in clust_list:
+            if float(c_boot) > 1:
+                import random
+                from joblib import Memory
+                from joblib.externals.loky import get_reusable_executor
+                print(
+                    f"Performing circular block bootstrapping with {c_boot}"
+                    f" iterations..."
+                )
+                ts_data, block_size = nip.prep_boot()
+
+                cache_dir = tempfile.mkdtemp()
+                memory = Memory(cache_dir, verbose=0)
+                ts_data = memory.cache(ts_data)
+
+                def create_bs_imgs(ts_data, block_size, clust_mask_corr_img):
+                    import nibabel as nib
+                    from nilearn.masking import unmask
+                    from pynets.fmri.estimation import timeseries_bootstrap
+                    boot_series = timeseries_bootstrap(
+                        ts_data.func, block_size)[0].astype('float32')
+                    return unmask(boot_series, clust_mask_corr_img)
+
+                def run_bs_iteration(i, ts_data, work_dir, local_corr,
+                                     clust_type, _local_conn_mat_path,
+                                     num_conn_comps, _clust_mask_corr_img,
+                                     _standardize, _detrending, k,
+                                     _local_conn, conf, _dir_path,
+                                     _conn_comps):
+                    import os
+                    import time
+                    import gc
+                    from pynets.fmri.clustering import parcellate
+                    print(f"\nBootstrapped iteration: {i}")
+                    out_path = f"{work_dir}/boot_parc_tmp_{str(i)}.nii.gz"
+
+                    boot_img = create_bs_imgs(ts_data, block_size,
+                                              _clust_mask_corr_img)
+                    try:
+                        parcellation = parcellate(boot_img, local_corr,
+                                                  clust_type,
+                                                  _local_conn_mat_path,
+                                                  num_conn_comps,
+                                                  _clust_mask_corr_img,
+                                                  _standardize,
+                                                  _detrending, k, _local_conn,
+                                                  conf, _dir_path,
+                                                  _conn_comps)
+                        parcellation.to_filename(out_path)
+                        parcellation.uncache()
+                        boot_img.uncache()
+                        gc.collect()
+                    except BaseException:
+                        boot_img.uncache()
+                        gc.collect()
+                        return None
+                    _clust_mask_corr_img.uncache()
+                    return out_path
+
+                time.sleep(random.randint(1, 5))
+                counter = 0
+                boot_parcellations = []
+                while float(counter) < float(c_boot):
+                    with Parallel(n_jobs=nthreads, max_nbytes='8000M',
+                                  backend='loky', mmap_mode='r+',
+                                  temp_folder=cache_dir,
+                                  verbose=10) as parallel:
+                        iter_bootedparcels = parallel(
+                            delayed(run_bs_iteration)(
+                                i, ts_data, runtime.cwd, nip.local_corr,
+                                nip.clust_type, nip._local_conn_mat_path,
+                                nip.num_conn_comps, nip._clust_mask_corr_img,
+                                nip._standardize, nip._detrending, nip.k,
+                                nip._local_conn, nip.conf, nip._dir_path,
+                                nip._conn_comps) for i in
+                            range(c_boot))
+
+                        boot_parcellations.extend([i for i in
+                                                   iter_bootedparcels if
+                                                   i is not None])
+                        counter = len(boot_parcellations)
+                        del iter_bootedparcels
+                        gc.collect()
+
+                print('Bootstrapped samples complete:')
+                print(boot_parcellations)
+                print("Creating spatially-constrained consensus "
+                      "parcellation...")
+                consensus_parcellation = clustering.ensemble_parcellate(
+                    boot_parcellations,
+                    int(self.inputs.k)
+                )
+                nib.save(consensus_parcellation, nip.parcellation)
+                memory.clear(warn=False)
+                shutil.rmtree(cache_dir, ignore_errors=True)
+                del parallel, memory, cache_dir
+                get_reusable_executor().shutdown(wait=True)
+                gc.collect()
+
+                for i in boot_parcellations:
+                    if i is not None:
+                        if os.path.isfile(i):
+                            os.system(f"rm -f {i} &")
+            else:
+                print(
+                    "Creating spatially-constrained parcellation...")
+                out_path = f"{runtime.cwd}/{atlas}_{str(self.inputs.k)}.nii.gz"
+                func_img = nib.load(out_name_func_file)
+                parcellation = clustering.parcellate(func_img,
+                                                     self.inputs.local_corr,
+                                                     self.inputs.clust_type,
+                                                     nip._local_conn_mat_path,
+                                                     nip.num_conn_comps,
+                                                     nip._clust_mask_corr_img,
+                                                     nip._standardize,
+                                                     nip._detrending, nip.k,
+                                                     nip._local_conn,
+                                                     nip.conf, nip._dir_path,
+                                                     nip._conn_comps)
+                parcellation.to_filename(out_path)
+
+        else:
+            raise ValueError(
+                "Clustering method not recognized. See: "
+                "https://nilearn.github.io/modules/generated/"
+                "nilearn.regions.Parcellations."
+                "html#nilearn.regions.Parcellations")
+
+        # Give it a minute
+        ix = 0
+        while not os.path.isfile(nip.parcellation) and ix < 60:
+            print('Waiting for clustered parcellation...')
+            time.sleep(1)
+            ix += 1
+
+        if not os.path.isfile(nip.parcellation):
+            raise FileNotFoundError(f"Parcellation clustering failed for"
+                                    f" {nip.parcellation}")
+
+        self._results["atlas"] = atlas
+        self._results["parcellation"] = nip.parcellation
+        self._results["clust_mask"] = clust_mask_in_t1w_path
+        self._results["k"] = self.inputs.k
+        self._results["clust_type"] = self.inputs.clust_type
+        self._results["clustering"] = True
+        self._results["func_file"] = self.inputs.func_file
+
+        reg_tmp = [
+            t1w_brain_tmp_path,
+            mni2t1w_warp_tmp_path,
+            mni2t1_xfm_tmp_path,
+            template_tmp_path,
+            out_name_func_file
+        ]
+        for j in reg_tmp:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        gc.collect()
+
+        return runtime
+
+
+class _ExtractTimeseriesInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for ExtractTimeseries"""
+
+    conf = traits.Any(mandatory=False)
+    func_file = File(exists=True, mandatory=True)
+    coords = traits.Any(mandatory=True)
+    roi = traits.Any(mandatory=False)
+    dir_path = Directory(exists=True, mandatory=True)
+    ID = traits.Any(mandatory=True)
+    subnet = traits.Any(mandatory=False)
+    smooth = traits.Any(mandatory=True)
+    atlas = traits.Any(mandatory=False)
+    parcellation = traits.Any(mandatory=False)
+    labels = traits.Any(mandatory=True)
+    hpass = traits.Any(mandatory=True)
+    mask = traits.Any(mandatory=False)
+    parc = traits.Bool()
+    node_radius = traits.Any(mandatory=False)
+    net_parcels_nii_path = traits.Any(mandatory=False)
+    extract_strategy = traits.Str("mean", mandatory=False, usedefault=True)
+
+
+class _ExtractTimeseriesOutputSpec(TraitedSpec):
+    """Output interface wrapper for ExtractTimeseries"""
+
+    ts_within_nodes = traits.Any(mandatory=True)
+    node_radius = traits.Any(mandatory=True)
+    smooth = traits.Any(mandatory=True)
+    dir_path = Directory(exists=True, mandatory=True)
+    atlas = traits.Any(mandatory=False)
+    parcellation = traits.Any(mandatory=False)
+    labels = traits.Any(mandatory=True)
+    coords = traits.Any(mandatory=True)
+    hpass = traits.Any(mandatory=True)
+    roi = traits.Any(mandatory=True)
+    extract_strategy = traits.Any(mandatory=False)
+
+
+class ExtractTimeseries(SimpleInterface):
+    """Interface wrapper for ExtractTimeseries"""
+
+    input_spec = _ExtractTimeseriesInputSpec
+    output_spec = _ExtractTimeseriesOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        from pynets.fmri import estimation
+        from pynets.core.utils import decompress_nifti
+
+        # Decompressing each image to facilitate more seamless memory mapping
+        if self.inputs.net_parcels_nii_path:
+            out_name_net_parcels_nii_path = fname_presuffix(
+                self.inputs.net_parcels_nii_path, suffix="_tmp",
+                newpath=runtime.cwd)
+            copyfile(
+                self.inputs.net_parcels_nii_path,
+                out_name_net_parcels_nii_path,
+                copy=True,
+                use_hardlink=False,
+            )
+            out_name_net_parcels_nii_path = decompress_nifti(
+                out_name_net_parcels_nii_path)
+        else:
+            out_name_net_parcels_nii_path = None
+
+        if self.inputs.mask:
+            out_name_mask = fname_presuffix(
+                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mask,
+                out_name_mask,
+                copy=True,
+                use_hardlink=False)
+            out_name_mask = decompress_nifti(out_name_mask)
+        else:
+            out_name_mask = None
+
+        out_name_func_file = fname_presuffix(
+            self.inputs.func_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.func_file,
+            out_name_func_file,
+            copy=True,
+            use_hardlink=False)
+        out_name_func_file = decompress_nifti(out_name_func_file)
+
+        if self.inputs.conf:
+            out_name_conf = fname_presuffix(
+                self.inputs.conf, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.conf,
+                out_name_conf,
+                copy=True,
+                use_hardlink=False)
+        else:
+            out_name_conf = None
+
+        te = estimation.TimeseriesExtraction(
+            net_parcels_nii_path=out_name_net_parcels_nii_path,
+            node_radius=self.inputs.node_radius,
+            conf=out_name_conf,
+            func_file=out_name_func_file,
+            roi=self.inputs.roi,
+            dir_path=self.inputs.dir_path,
+            ID=self.inputs.ID,
+            subnet=self.inputs.subnet,
+            smooth=self.inputs.smooth,
+            hpass=self.inputs.hpass,
+            mask=out_name_mask,
+            extract_strategy=self.inputs.extract_strategy,
+        )
+
+        te.prepare_inputs()
+
+        te.extract_ts_parc()
+
+        te.save_and_cleanup()
+
+        try:
+            assert (
+                len(self.inputs.coords)
+                == len(self.inputs.labels)
+                == te.ts_within_nodes.shape[1]
+            )
+        except AssertionError as e:
+            e.args += ('Coords: ', len(self.inputs.coords),
+                       self.inputs.coords, 'Labels:',
+                       len(self.inputs.labels),
+                       self.inputs.labels, te.ts_within_nodes.shape)
+
+        self._results["ts_within_nodes"] = te.ts_within_nodes
+        self._results["node_radius"] = te.node_radius
+        self._results["smooth"] = te.smooth
+        self._results["extract_strategy"] = te.extract_strategy
+        self._results["dir_path"] = te.dir_path
+        self._results["atlas"] = self.inputs.atlas
+        self._results["parcellation"] = self.inputs.parcellation
+        self._results["labels"] = self.inputs.labels
+        self._results["coords"] = self.inputs.coords
+        self._results["hpass"] = te.hpass
+        self._results["roi"] = self.inputs.roi
+
+        del te
+        gc.collect()
+
+        return runtime
diff --git a/pynets/plotting/__init__.py b/pynets/plotting/__init__.py
index 75068549..44ba4dd7 100644
--- a/pynets/plotting/__init__.py
+++ b/pynets/plotting/__init__.py
@@ -4,6 +4,6 @@
 __email__ = "dpisner@utexas.edu"
 
 import warnings
-from . import *
+from . import adjacency, brain, connectogram, interfaces
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/plotting/plot_graphs.py b/pynets/plotting/adjacency.py
similarity index 96%
rename from pynets/plotting/plot_graphs.py
rename to pynets/plotting/adjacency.py
index 96920614..3e5cfa4f 100644
--- a/pynets/plotting/plot_graphs.py
+++ b/pynets/plotting/adjacency.py
@@ -3,12 +3,11 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
-import numpy as np
-import tkinter
+
 import matplotlib
+import numpy as np
 
 warnings.filterwarnings("ignore")
 matplotlib.use("agg")
@@ -32,7 +31,6 @@ def plot_conn_mat(conn_matrix, labels, out_path_fig, cmap, binarized=False,
     warnings.filterwarnings("ignore")
     import matplotlib
     matplotlib.use('Agg')
-    import mplcyberpunk
     from matplotlib import pyplot as plt
     plt.style.use("cyberpunk")
     from matplotlib import pyplot as plt
@@ -78,6 +76,7 @@ def plot_conn_mat(conn_matrix, labels, out_path_fig, cmap, binarized=False,
     return
 
 
+# noinspection PyUnresolvedReferences
 def plot_community_conn_mat(
         conn_matrix,
         labels,
@@ -105,7 +104,6 @@ def plot_community_conn_mat(
     import matplotlib
     from matplotlib import pyplot as plt
     matplotlib.use("agg")
-    import mplcyberpunk
     plt.style.use("cyberpunk")
     import matplotlib.patches as patches
     import matplotlib.ticker as mticker
@@ -249,10 +247,8 @@ def plot_conn_mat_func(
     matplotlib.use('Agg')
     import matplotlib.pyplot as plt
     from pynets.core.utils import load_runconfig
-    import sys
     import networkx as nx
     import os.path as op
-    from pynets.plotting import plot_graphs
 
     out_path_fig = \
         "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % \
@@ -295,16 +291,17 @@ def plot_conn_mat_func(
     except KeyError as e:
         print(e,
               "Plotting configuration not successfully extracted from"
-              " runconfig.yaml"
+              " advanced.yaml"
               )
 
-    plot_graphs.plot_conn_mat(
+    plot_conn_mat(
         conn_matrix, labels, out_path_fig, cmap=plt.get_cmap(cmap_name)
     )
 
     # Plot community adj. matrix
     try:
-        from pynets.stats.netstats import community_resolution_selection
+        from pynets.statistics.individual.algorithms import \
+            community_resolution_selection
 
         G = nx.from_numpy_matrix(np.abs(conn_matrix))
         _, node_comm_aff_mat, resolution, num_comms = \
@@ -342,7 +339,7 @@ def plot_conn_mat_func(
              thr,
              ".png",
              )
-        plot_graphs.plot_community_conn_mat(
+        plot_community_conn_mat(
             conn_matrix,
             labels,
             out_path_fig_comm,
@@ -421,8 +418,7 @@ def plot_conn_mat_struct(
     matplotlib.use('Agg')
     import matplotlib.pyplot as plt
     from pynets.core.utils import load_runconfig
-    import sys
-    from pynets.plotting import plot_graphs
+    from pynets.plotting import adjacency
     import networkx as nx
     import os.path as op
 
@@ -470,16 +466,17 @@ def plot_conn_mat_struct(
     except KeyError as e:
         print(e,
               "Plotting configuration not successfully extracted from"
-              " runconfig.yaml"
+              " advanced.yaml"
               )
 
-    plot_graphs.plot_conn_mat(
+    adjacency.plot_conn_mat(
         conn_matrix, labels, out_path_fig, cmap=plt.get_cmap(cmap_name)
     )
 
     # Plot community adj. matrix
     try:
-        from pynets.stats.netstats import community_resolution_selection
+        from pynets.statistics.individual.algorithms import \
+            community_resolution_selection
 
         G = nx.from_numpy_matrix(np.abs(conn_matrix))
         _, node_comm_aff_mat, resolution, num_comms = \
@@ -520,7 +517,7 @@ def plot_conn_mat_struct(
                thr,
                ".png",
                )
-        plot_graphs.plot_community_conn_mat(
+        adjacency.plot_community_conn_mat(
             conn_matrix,
             labels,
             out_path_fig_comm,
diff --git a/pynets/plotting/plot_gen.py b/pynets/plotting/brain.py
similarity index 98%
rename from pynets/plotting/plot_gen.py
rename to pynets/plotting/brain.py
index 2590512b..63a9bd23 100644
--- a/pynets/plotting/plot_gen.py
+++ b/pynets/plotting/brain.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner (dPys)
 """
 import warnings
 import numpy as np
@@ -12,7 +11,6 @@
 if sys.platform.startswith('win') is False:
     import indexed_gzip
 import networkx as nx
-import os.path as op
 import tkinter
 import matplotlib
 import matplotlib.pyplot as plt
@@ -221,8 +219,8 @@ def create_gb_palette(
     import mplcyberpunk
     from matplotlib import colors
     from sklearn.preprocessing import minmax_scale
-    from pynets.stats.netstats import community_resolution_selection, \
-        prune_disconnected
+    from pynets.statistics.individual.algorithms import \
+        community_resolution_selection, prune_disconnected
 
     plt.style.use("cyberpunk")
 
@@ -435,8 +433,7 @@ def plot_all_func(
     import pickle
     from scipy.spatial import distance
     from pynets.core.utils import load_mat
-    from pynets.plotting import plot_gen, plot_graphs
-    from pynets.plotting.plot_gen import create_gb_palette
+    from pynets.plotting import adjacency, connectogram
 
     ch2better_loc = pkg_resources.resource_filename(
         "pynets", "templates/ch2better.nii.gz"
@@ -494,7 +491,7 @@ def plot_all_func(
     except KeyError as e:
         print(e,
               "Plotting configuration not successfully extracted "
-              "from runconfig.yaml"
+              "from advanced.yaml"
               )
 
     if not isinstance(coords, list):
@@ -536,7 +533,7 @@ def plot_all_func(
         if connectogram is True:
             if len(conn_matrix) > 20:
                 try:
-                    plot_gen.plot_connectogram(
+                    connectogram.plot_connectogram(
                         conn_matrix, conn_model, atlas, namer_dir, ID,
                         subnet, labels)
                 except RuntimeWarning:
@@ -552,7 +549,7 @@ def plot_all_func(
             node_radius = "parc"
 
         if adjacency is True:
-            plot_graphs.plot_conn_mat_func(
+            adjacency.plot_conn_mat_func(
                 conn_matrix,
                 conn_model,
                 atlas,
@@ -818,8 +815,7 @@ def plot_all_struct(
     import pickle
     from scipy.spatial import distance
     from pynets.core.utils import load_mat
-    from pynets.plotting import plot_gen, plot_graphs
-    from pynets.plotting.plot_gen import create_gb_palette
+    from pynets.plotting import adjacency
     import mplcyberpunk
 
     plt.style.use("cyberpunk")
@@ -894,7 +890,7 @@ def plot_all_struct(
         if connectogram is True:
             if len(conn_matrix) > 20:
                 try:
-                    plot_gen.plot_connectogram(
+                    connectogram.plot_connectogram(
                         conn_matrix, conn_model, atlas, namer_dir, ID,
                         subnet, labels)
                 except RuntimeWarning:
@@ -910,7 +906,7 @@ def plot_all_struct(
             node_radius = "parc"
 
         if adjacency is True:
-            plot_graphs.plot_conn_mat_struct(
+            adjacency.plot_conn_mat_struct(
                 conn_matrix,
                 conn_model,
                 atlas,
@@ -1133,7 +1129,6 @@ def plot_all_struct_func(mG_path, namer_dir, name, modality_paths, metadata):
     from matplotlib import pyplot as plt
     from nilearn import plotting as niplot
     from pynets.core import thresholding
-    from pynets.plotting.plot_gen import create_gb_palette
     import mplcyberpunk
     from scipy.spatial import distance
 
diff --git a/pynets/plotting/connectogram.py b/pynets/plotting/connectogram.py
new file mode 100644
index 00000000..24c2461b
--- /dev/null
+++ b/pynets/plotting/connectogram.py
@@ -0,0 +1,288 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue Nov  7 10:40:07 2017
+Copyright (C) 2017
+"""
+import warnings
+import numpy as np
+import sys
+if sys.platform.startswith('win') is False:
+    import indexed_gzip
+import networkx as nx
+import tkinter
+import matplotlib
+
+warnings.filterwarnings("ignore")
+matplotlib.use("agg")
+
+
+def plot_connectogram(
+    conn_matrix,
+    conn_model,
+    dir_path,
+    ID,
+    subnet,
+    labels,
+    comm="nodes",
+    color_scheme="interpolateBlues",
+    prune=False,
+):
+    """
+    Plot a connectogram for a given connectivity matrix.
+
+    Parameters
+    ----------
+    conn_matrix : array
+        NxN matrix.
+    conn_model : str
+       Connectivity estimation model (e.g. corr for correlation, cov for
+       covariance, sps for precision covariance, partcorr for
+       partial correlation). sps type is used by default.
+    dir_path : str
+        Path to directory containing subject derivative data for given run.
+    ID : str
+        A subject id or other unique identifier.
+    subnet : str
+        Resting-state network based on Yeo-7 and Yeo-17 naming
+        (e.g. 'Default') used to filter nodes in the study of brain subgraphs.
+    labels : list
+        List of string labels corresponding to ROI nodes.
+    comm : str, optional default: 'nodes'
+        Communitity setting, either 'nodes' or 'links'
+    color_scheme : str, optional, default: 'interpolateBlues'
+        Color scheme in json.
+    prune : bool
+        Indicates whether to prune final graph of disconnected nodes/isolates.
+
+    """
+    import json
+    from pathlib import Path
+    from networkx.readwrite import json_graph
+    from pynets.core.thresholding import normalize
+    from pynets.statistics.individual.algorithms import most_important, \
+        link_communities, community_resolution_selection
+
+    # from scipy.cluster.hierarchy import linkage, fcluster
+    from nipype.utils.filemanip import save_json
+
+    conn_matrix = normalize(conn_matrix)
+    G = nx.from_numpy_matrix(np.abs(conn_matrix))
+    if prune is True:
+        [G, pruned_nodes] = most_important(G)
+        conn_matrix = nx.to_numpy_array(G)
+
+        pruned_nodes.sort(reverse=True)
+        for j in pruned_nodes:
+            del labels[labels.index(labels[j])]
+
+    if comm == "nodes" and len(conn_matrix) > 40:
+        G = nx.from_numpy_matrix(np.abs(conn_matrix))
+        _, node_comm_aff_mat, resolution, num_comms = \
+            community_resolution_selection(G)
+        clust_levels = len(node_comm_aff_mat)
+        clust_levels_tmp = int(clust_levels) - 1
+        mask_mat = np.squeeze(np.array([node_comm_aff_mat == 0]).astype("int"))
+        label_arr = (
+            node_comm_aff_mat *
+            np.expand_dims(
+                np.arange(
+                    1,
+                    clust_levels +
+                    1),
+                axis=1) +
+            mask_mat)
+    elif comm == "links" and len(conn_matrix) > 40:
+        # Plot link communities
+        link_comm_aff_mat = link_communities(
+            conn_matrix, type_clustering="single")[0]
+        print(f"{'Found '}{str(len(link_comm_aff_mat))}{' communities...'}")
+        clust_levels = len(link_comm_aff_mat)
+        clust_levels_tmp = int(clust_levels) - 1
+        mask_mat = np.squeeze(np.array([link_comm_aff_mat == 0]).astype("int"))
+        label_arr = (
+            link_comm_aff_mat *
+            np.expand_dims(
+                np.arange(
+                    1,
+                    clust_levels +
+                    1),
+                axis=1) +
+            mask_mat)
+    else:
+        return
+
+    def _get_node_label(node_idx, labels, clust_levels_tmp):
+        """
+        Tag a label to a given node based on its community/cluster assignment
+        """
+        from collections import OrderedDict
+
+        def _write_roman(num):
+            """
+            Create community/cluster assignments using a Roman-Numeral
+            generator.
+            """
+            roman = OrderedDict()
+            roman[1000] = "M"
+            roman[900] = "CM"
+            roman[500] = "D"
+            roman[400] = "CD"
+            roman[100] = "C"
+            roman[90] = "XC"
+            roman[50] = "L"
+            roman[40] = "XL"
+            roman[10] = "X"
+            roman[9] = "IX"
+            roman[5] = "V"
+            roman[4] = "IV"
+            roman[1] = "I"
+
+            def roman_num(num):
+                """
+
+                :param num:
+                """
+                for r in roman.keys():
+                    x, y = divmod(num, r)
+                    yield roman[r] * x
+                    num -= r * x
+                    if num > 0:
+                        roman_num(num)
+                    else:
+                        break
+
+            return "".join([a for a in roman_num(num)])
+
+        rn_list = []
+        node_idx = node_idx - 1
+        node_labels = labels[:, node_idx]
+        for k in [int(l) for i, l in enumerate(node_labels)]:
+            rn_list.append(json.dumps(_write_roman(k)))
+        abet = rn_list
+        node_lab_alph = ".".join(
+            ["{}{}".format(abet[i], int(l)) for i, l in enumerate(node_labels)]
+        ) + ".{}".format(labels[node_idx])
+        return node_lab_alph
+
+    output = []
+
+    adj_dict = {}
+    for i in list(G.adjacency()):
+        source = list(i)[0]
+        target = list(list(i)[1])
+        adj_dict[source] = target
+
+    for node_idx, connections in adj_dict.items():
+        weight_vec = []
+        for i in connections:
+            wei = G.get_edge_data(node_idx, int(i))["weight"]
+            weight_vec.append(wei)
+        entry = {}
+        nodes_label = _get_node_label(node_idx, label_arr, clust_levels_tmp)
+        entry["name"] = nodes_label
+        entry["size"] = len(connections)
+        entry["imports"] = [
+            _get_node_label(int(d) - 1, label_arr, clust_levels_tmp)
+            for d in connections
+        ]
+        entry["weights"] = weight_vec
+        output.append(entry)
+
+    if subnet:
+        json_file_name = (
+            f"{str(ID)}{'_'}{subnet}{'_connectogram_'}{conn_model}"
+            f"{'_network.json'}"
+        )
+        json_fdg_file_name = (
+            f"{str(ID)}{'_'}{subnet}{'_fdg_'}{conn_model}{'_network.json'}"
+        )
+        connectogram_plot = f"{dir_path}{'/'}{json_file_name}"
+        fdg_js_sub = f"{dir_path}{'/'}{str(ID)}{'_'}{subnet}{'_fdg_'}" \
+                     f"{conn_model}{'_network.js'}"
+        fdg_js_sub_name = f"{str(ID)}{'_'}{subnet}{'_fdg_'}{conn_model}" \
+                          f"{'_network.js'}"
+        connectogram_js_sub = (
+            f"{dir_path}/{str(ID)}_{subnet}_connectogram_{conn_model}"
+            f"_network.js"
+        )
+        connectogram_js_name = (
+            f"{str(ID)}{'_'}{subnet}{'_connectogram_'}{conn_model}"
+            f"{'_network.js'}"
+        )
+    else:
+        json_file_name = f"{str(ID)}{'_connectogram_'}{conn_model}{'.json'}"
+        json_fdg_file_name = f"{str(ID)}{'_fdg_'}{conn_model}{'.json'}"
+        connectogram_plot = f"{dir_path}{'/'}{json_file_name}"
+        connectogram_js_sub = (
+            f"{dir_path}{'/'}{str(ID)}{'_connectogram_'}{conn_model}{'.js'}"
+        )
+        fdg_js_sub = f"{dir_path}{'/'}{str(ID)}{'_fdg_'}{conn_model}{'.js'}"
+        fdg_js_sub_name = f"{str(ID)}{'_fdg_'}{conn_model}{'.js'}"
+        connectogram_js_name = f"{str(ID)}{'_connectogram_'}{conn_model}" \
+                               f"{'.js'}"
+    save_json(connectogram_plot, output)
+
+    # Force-directed graphing
+    G = nx.from_numpy_matrix(
+        np.round(
+            np.abs(conn_matrix).astype("float64"),
+            6))
+    data = json_graph.node_link_data(G)
+    data.pop("directed", None)
+    data.pop("graph", None)
+    data.pop("multigraph", None)
+    for k in range(len(data["links"])):
+        data["links"][k]["value"] = data["links"][k].pop("weight")
+    for k in range(len(data["nodes"])):
+        data["nodes"][k]["id"] = str(data["nodes"][k]["id"])
+    for k in range(len(data["links"])):
+        data["links"][k]["source"] = str(data["links"][k]["source"])
+        data["links"][k]["target"] = str(data["links"][k]["target"])
+
+    # Add community structure
+    for k in range(len(data["nodes"])):
+        data["nodes"][k]["group"] = str(label_arr[0][k])
+
+    # Add node labels
+    for k in range(len(data["nodes"])):
+        data["nodes"][k]["name"] = str(labels[k])
+
+    out_file = f"{dir_path}{'/'}{str(json_fdg_file_name)}"
+    save_json(out_file, data)
+
+    # Copy index.html and json to dir_path
+    conn_js_path = str(Path(__file__).parent / "connectogram.js")
+    index_html_path = str(Path(__file__).parent / "index.html")
+    fdg_replacements_js = {"FD_graph.json": str(json_fdg_file_name)}
+    replacements_html = {
+        "connectogram.js": str(connectogram_js_name),
+        "fdg.js": str(fdg_js_sub_name),
+    }
+    fdg_js_path = str(Path(__file__).parent / "fdg.js")
+    with open(index_html_path) as infile, open(
+        str(dir_path + "/index.html"), "w"
+    ) as outfile:
+        for line in infile:
+            for src, target in replacements_html.items():
+                line = line.replace(src, target)
+            outfile.write(line)
+
+    replacements_js = {
+        "template.json": str(json_file_name),
+        "interpolateCool": str(color_scheme),
+    }
+    with open(conn_js_path) as infile, open(connectogram_js_sub, "w") as \
+        outfile:
+        for line in infile:
+            for src, target in replacements_js.items():
+                line = line.replace(src, target)
+            outfile.write(line)
+
+    with open(fdg_js_path) as infile, open(fdg_js_sub, "w") as outfile:
+        for line in infile:
+            for src, target in fdg_replacements_js.items():
+                line = line.replace(src, target)
+            outfile.write(line)
+
+    return
diff --git a/pynets/plotting/interfaces.py b/pynets/plotting/interfaces.py
new file mode 100755
index 00000000..3a1a108a
--- /dev/null
+++ b/pynets/plotting/interfaces.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Nov 10 15:44:46 2017
+Copyright (C) 2017
+"""
+import warnings
+import numpy as np
+import sys
+if sys.platform.startswith('win') is False:
+    import indexed_gzip
+from nipype.interfaces.base import (
+    BaseInterfaceInputSpec,
+    traits,
+    SimpleInterface,
+    Directory,
+)
+
+warnings.filterwarnings("ignore")
+
+
+class _PlotStructInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for PlotStruct"""
+
+    conn_matrix = traits.Any()
+    conn_model = traits.Str(mandatory=True)
+    atlas = traits.Any(mandatory=False)
+    dir_path = Directory(exists=True, mandatory=True)
+    ID = traits.Any(mandatory=True)
+    subnet = traits.Any(mandatory=True)
+    labels = traits.Array(mandatory=True)
+    roi = traits.Any(mandatory=True)
+    coords = traits.Array(mandatory=True)
+    thr = traits.Any(mandatory=True)
+    node_radius = traits.Any(mandatory=True)
+    edge_threshold = traits.Any(mandatory=True)
+    prune = traits.Any(mandatory=True)
+    parcellation = traits.Any(mandatory=False)
+    target_samples = traits.Any(mandatory=True)
+    norm = traits.Any(mandatory=True)
+    binary = traits.Bool(mandatory=True)
+    track_type = traits.Any(mandatory=True)
+    directget = traits.Any(mandatory=True)
+    min_length = traits.Any(mandatory=True)
+    error_margin = traits.Any(mandatory=True)
+
+
+class _PlotStructOutputSpec(BaseInterfaceInputSpec):
+    """Output interface wrapper for PlotStruct"""
+
+    out = traits.Str
+
+
+class PlotStruct(SimpleInterface):
+    """Interface wrapper for PlotStruct"""
+
+    input_spec = _PlotStructInputSpec
+    output_spec = _PlotStructOutputSpec
+
+    def _run_interface(self, runtime):
+        from pynets.plotting import brain
+
+        if isinstance(self.inputs.conn_matrix, str):
+            self.inputs.conn_matrix = np.load(self.inputs.conn_matrix)
+
+        assert (
+            len(self.inputs.coords)
+            == len(self.inputs.labels)
+            == self.inputs.conn_matrix.shape[0]
+        )
+        if self.inputs.coords.ndim == 1:
+            print("Only 1 node detected. Plotting is not applicable...")
+        else:
+            brain.plot_all_struct(
+                self.inputs.conn_matrix,
+                self.inputs.conn_model,
+                self.inputs.atlas,
+                self.inputs.dir_path,
+                self.inputs.ID,
+                self.inputs.subnet,
+                self.inputs.labels.tolist(),
+                self.inputs.roi,
+                [tuple(coord) for coord in self.inputs.coords.tolist()],
+                self.inputs.thr,
+                self.inputs.node_radius,
+                self.inputs.edge_threshold,
+                self.inputs.prune,
+                self.inputs.parcellation,
+                self.inputs.target_samples,
+                self.inputs.norm,
+                self.inputs.binary,
+                self.inputs.track_type,
+                self.inputs.directget,
+                self.inputs.min_length,
+                self.inputs.error_margin
+            )
+
+        self._results["out"] = "None"
+
+        return runtime
+
+
+class _PlotFuncInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for PlotFunc"""
+
+    conn_matrix = traits.Any()
+    conn_model = traits.Str(mandatory=True)
+    atlas = traits.Any(mandatory=False)
+    dir_path = Directory(exists=True, mandatory=True)
+    ID = traits.Any(mandatory=True)
+    subnet = traits.Any(mandatory=True)
+    labels = traits.Array(mandatory=True)
+    roi = traits.Any(mandatory=True)
+    coords = traits.Array(mandatory=True)
+    thr = traits.Any(mandatory=True)
+    node_radius = traits.Any(mandatory=True)
+    edge_threshold = traits.Any(mandatory=True)
+    smooth = traits.Any(mandatory=True)
+    prune = traits.Any(mandatory=True)
+    parcellation = traits.Any(mandatory=False)
+    norm = traits.Any(mandatory=True)
+    binary = traits.Bool(mandatory=True)
+    hpass = traits.Any(mandatory=True)
+    extract_strategy = traits.Any(mandatory=True)
+    edge_color_override = traits.Bool(mandatory=True)
+
+
+class _PlotFuncOutputSpec(BaseInterfaceInputSpec):
+    """Output interface wrapper for PlotFunc"""
+
+    out = traits.Str
+
+
+class PlotFunc(SimpleInterface):
+    """Interface wrapper for PlotFunc"""
+
+    input_spec = _PlotFuncInputSpec
+    output_spec = _PlotFuncOutputSpec
+
+    def _run_interface(self, runtime):
+        from pynets.plotting import brain
+
+        if isinstance(self.inputs.conn_matrix, str):
+            self.inputs.conn_matrix = np.load(self.inputs.conn_matrix)
+
+        assert (
+            len(self.inputs.coords)
+            == len(self.inputs.labels)
+            == self.inputs.conn_matrix.shape[0]
+        )
+
+        if self.inputs.coords.ndim == 1:
+            print("Only 1 node detected. Plotting not applicable...")
+        else:
+            brain.plot_all_func(
+                self.inputs.conn_matrix,
+                self.inputs.conn_model,
+                self.inputs.atlas,
+                self.inputs.dir_path,
+                self.inputs.ID,
+                self.inputs.subnet,
+                self.inputs.labels.tolist(),
+                self.inputs.roi,
+                [tuple(coord) for coord in self.inputs.coords.tolist()],
+                self.inputs.thr,
+                self.inputs.node_radius,
+                self.inputs.edge_threshold,
+                self.inputs.smooth,
+                self.inputs.prune,
+                self.inputs.parcellation,
+                self.inputs.norm,
+                self.inputs.binary,
+                self.inputs.hpass,
+                self.inputs.extract_strategy,
+                self.inputs.edge_color_override,
+            )
+
+        self._results["out"] = "None"
+
+        return runtime
diff --git a/pynets/registration/__init__.py b/pynets/registration/__init__.py
index 75068549..ac8482ff 100644
--- a/pynets/registration/__init__.py
+++ b/pynets/registration/__init__.py
@@ -4,6 +4,6 @@
 __email__ = "dpisner@utexas.edu"
 
 import warnings
-from . import *
+from . import utils, register, interfaces
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/registration/interfaces.py b/pynets/registration/interfaces.py
new file mode 100755
index 00000000..f49e06ee
--- /dev/null
+++ b/pynets/registration/interfaces.py
@@ -0,0 +1,1614 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Nov 10 15:44:46 2017
+Copyright (C) 2017
+"""
+import warnings
+import numpy as np
+import sys
+if sys.platform.startswith('win') is False:
+    import indexed_gzip
+import nibabel as nib
+from nipype.interfaces.base import (
+    BaseInterfaceInputSpec,
+    TraitedSpec,
+    File,
+    traits,
+    SimpleInterface,
+    Directory,
+)
+
+warnings.filterwarnings("ignore")
+
+
+class _RegisterDWIInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterDWI"""
+
+    fa_path = File(exists=True, mandatory=True)
+    ap_path = File(exists=True, mandatory=True)
+    B0_mask = File(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    gtab_file = File(exists=True, mandatory=True)
+    dwi_file = File(exists=True, mandatory=True)
+    in_dir = traits.Any()
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    mask = traits.Any(mandatory=False)
+    force_create_mask = traits.Bool(True, usedefault=True)
+    simple = traits.Bool(False, usedefault=True)
+    overwrite = traits.Bool(False, usedefault=True)
+
+
+class _RegisterDWIOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterDWI"""
+
+    wm_in_dwi = File(exists=True, mandatory=True)
+    gm_in_dwi = File(exists=True, mandatory=True)
+    vent_csf_in_dwi = File(exists=True, mandatory=True)
+    csf_mask_dwi = File(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    B0_mask = File(exists=True, mandatory=True)
+    ap_path = File(exists=True, mandatory=True)
+    gtab_file = File(exists=True, mandatory=True)
+    dwi_file = File(exists=True, mandatory=True)
+    basedir_path = Directory(exists=True, mandatory=True)
+    t1w2dwi = File(exists=True, mandatory=True)
+    t1w_brain_mask_in_dwi = traits.Any(mandatory=False)
+    t1_aligned_mni = traits.Any(mandatory=False)
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    t1wtissue2dwi_xfm = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    t1w_brain_mask = File(exists=True, mandatory=True)
+    t1w2dwi_bbr_xfm = File(exists=True, mandatory=True)
+    t1w2dwi_xfm = File(exists=True, mandatory=True)
+    wm_gm_int_in_dwi = File(exists=True, mandatory=True)
+
+
+class RegisterDWI(SimpleInterface):
+    """Interface wrapper for RegisterDWI to create T1w->MNI->DWI mappings."""
+
+    input_spec = _RegisterDWIInputSpec
+    output_spec = _RegisterDWIOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import time
+        import glob
+        import os
+        import os.path as op
+        from pynets.registration import register
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        from pynets.registration.utils import orient_reslice
+
+        fa_tmp_path = fname_presuffix(
+            self.inputs.fa_path, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.fa_path,
+            fa_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        ap_tmp_path = fname_presuffix(
+            self.inputs.ap_path, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.ap_path,
+            ap_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        B0_mask_tmp_path = fname_presuffix(
+            self.inputs.B0_mask, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.B0_mask,
+            B0_mask_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        anat_mask_existing = [
+            i
+            for i in glob.glob(f"{self.inputs.in_dir}"
+                               f"/*_desc-brain_mask.nii.gz")
+            if "MNI" not in i
+        ]
+
+        # Copy T1w mask, if provided, else use existing, if detected, else
+        # compute a fresh one
+        if self.inputs.mask:
+            mask_tmp_path = fname_presuffix(
+                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mask,
+                mask_tmp_path,
+                copy=True,
+                use_hardlink=False)
+        else:
+            if len(anat_mask_existing) > 0 and \
+                    self.inputs.mask is None and \
+                    op.isfile(anat_mask_existing[0]) and \
+                    self.inputs.force_create_mask is False:
+                mask_tmp_path = fname_presuffix(
+                    anat_mask_existing[0], suffix="_tmp", newpath=runtime.cwd
+                )
+                copyfile(
+                    anat_mask_existing[0],
+                    mask_tmp_path,
+                    copy=True,
+                    use_hardlink=False)
+                mask_tmp_path = orient_reslice(
+                    mask_tmp_path, runtime.cwd, self.inputs.vox_size
+                )
+            else:
+                mask_tmp_path = None
+
+        gm_mask_existing = glob.glob(
+            self.inputs.in_dir +
+            "/*_label-GM_probseg.nii.gz")
+        if len(gm_mask_existing) > 0:
+            gm_mask = fname_presuffix(gm_mask_existing[0], newpath=runtime.cwd)
+            copyfile(
+                gm_mask_existing[0],
+                gm_mask,
+                copy=True,
+                use_hardlink=False)
+            gm_mask = orient_reslice(
+                gm_mask, runtime.cwd, self.inputs.vox_size
+            )
+        else:
+            gm_mask = None
+
+        wm_mask_existing = glob.glob(
+            self.inputs.in_dir +
+            "/*_label-WM_probseg.nii.gz")
+        if len(wm_mask_existing) > 0:
+            wm_mask = fname_presuffix(wm_mask_existing[0], newpath=runtime.cwd)
+            copyfile(
+                wm_mask_existing[0],
+                wm_mask,
+                copy=True,
+                use_hardlink=False)
+            wm_mask = orient_reslice(
+                wm_mask, runtime.cwd, self.inputs.vox_size
+            )
+        else:
+            wm_mask = None
+
+        csf_mask_existing = glob.glob(
+            self.inputs.in_dir + "/*_label-CSF_probseg.nii.gz"
+        )
+        if len(csf_mask_existing) > 0:
+            csf_mask = fname_presuffix(
+                csf_mask_existing[0], newpath=runtime.cwd)
+            copyfile(
+                csf_mask_existing[0],
+                csf_mask,
+                copy=True,
+                use_hardlink=False)
+            csf_mask = orient_reslice(
+                csf_mask, runtime.cwd, self.inputs.vox_size
+            )
+        else:
+            csf_mask = None
+
+        anat_file_tmp_path = fname_presuffix(
+            self.inputs.anat_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.anat_file,
+            anat_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        reg = register.DmriReg(
+            basedir_path=runtime.cwd,
+            fa_path=fa_tmp_path,
+            ap_path=ap_tmp_path,
+            B0_mask=B0_mask_tmp_path,
+            anat_file=anat_file_tmp_path,
+            vox_size=self.inputs.vox_size,
+            template_name=self.inputs.template_name,
+            simple=self.inputs.simple,
+        )
+
+        # Generate T1w brain mask
+        reg.gen_mask(mask_tmp_path)
+
+        # Perform anatomical segmentation
+        reg.gen_tissue(wm_mask, gm_mask, csf_mask, self.inputs.overwrite)
+
+        # Align t1w to mni template
+        # from joblib import Memory
+        # import os
+        # location = f"{outdir}/joblib_" \
+        #            f"{self.inputs.anat_file.split('.nii')[0]}"
+        # os.makedirs(location, exist_ok=True)
+        # memory = Memory(location)
+        # t1w2mni_align = memory.cache(reg.t1w2mni_align)
+        # t1w2mni_align()
+        reg.t1w2mni_align()
+        time.sleep(0.5)
+
+        if (self.inputs.overwrite is True) or (
+                op.isfile(reg.t1w2dwi) is False):
+            # Align t1w to dwi
+            reg.t1w2dwi_align()
+            time.sleep(0.5)
+
+        if (self.inputs.overwrite is True) or (
+            op.isfile(reg.wm_gm_int_in_dwi) is False
+        ):
+            # Align tissue
+            reg.tissue2dwi_align()
+            time.sleep(0.5)
+
+        self._results["wm_in_dwi"] = reg.wm_in_dwi
+        self._results["gm_in_dwi"] = reg.gm_in_dwi
+        self._results["vent_csf_in_dwi"] = reg.vent_csf_in_dwi
+        self._results["csf_mask_dwi"] = reg.csf_mask_dwi
+        self._results["anat_file"] = anat_file_tmp_path
+        self._results["t1w2dwi"] = reg.t1w2dwi
+        self._results["B0_mask"] = B0_mask_tmp_path
+        self._results["ap_path"] = ap_tmp_path
+        self._results["gtab_file"] = self.inputs.gtab_file
+        self._results["dwi_file"] = self.inputs.dwi_file
+        self._results["basedir_path"] = runtime.cwd
+        self._results["t1w_brain_mask_in_dwi"] = reg.t1w_brain_mask_in_dwi
+        self._results["t1_aligned_mni"] = reg.t1_aligned_mni
+        self._results["t1w_brain"] = reg.t1w_brain
+        self._results["mni2t1w_warp"] = reg.mni2t1w_warp
+        self._results["t1wtissue2dwi_xfm"] = reg.t1wtissue2dwi_xfm
+        self._results["mni2t1_xfm"] = reg.mni2t1_xfm
+        self._results["t1w_brain_mask"] = reg.t1w_brain_mask
+        self._results["t1w2dwi_bbr_xfm"] = reg.t1w2dwi_bbr_xfm
+        self._results["t1w2dwi_xfm"] = reg.t1w2dwi_xfm
+        self._results["wm_gm_int_in_dwi"] = reg.wm_gm_int_in_dwi
+
+        reg_tmp = [
+            fa_tmp_path,
+            mask_tmp_path,
+            reg.warp_t1w2mni,
+            reg.t1w_head,
+            reg.wm_edge,
+            reg.vent_mask_dwi,
+            reg.vent_mask_t1w,
+            reg.corpuscallosum_mask_t1w,
+            reg.corpuscallosum_dwi
+        ]
+        for j in reg_tmp:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        gc.collect()
+
+        return runtime
+
+
+class _RegisterAtlasDWIInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterAtlasDWI"""
+
+    atlas = traits.Any(mandatory=False)
+    subnet = traits.Any(mandatory=True)
+    parcellation4d = traits.Any(mandatory=True)
+    parcellation = traits.Any(mandatory=False)
+    basedir_path = Directory(exists=True, mandatory=True)
+    node_radius = traits.Any(mandatory=True)
+    fa_path = File(exists=True, mandatory=True)
+    ap_path = File(exists=True, mandatory=True)
+    B0_mask = File(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    coords = traits.Any(mandatory=True)
+    labels = traits.Any(mandatory=True)
+    gm_in_dwi = File(exists=True, mandatory=True)
+    vent_csf_in_dwi = File(exists=True, mandatory=True)
+    wm_in_dwi = File(exists=True, mandatory=True)
+    gtab_file = File(exists=True, mandatory=True)
+    dwi_file = File(exists=True, mandatory=True)
+    mask = traits.Any(mandatory=False)
+    waymask = traits.Any(mandatory=False)
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    t1wtissue2dwi_xfm = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    t1w_brain_mask = File(exists=True, mandatory=True)
+    t1_aligned_mni = File(exists=True, mandatory=True)
+    t1w2dwi_bbr_xfm = File(exists=True, mandatory=True)
+    t1w2dwi_xfm = File(exists=True, mandatory=True)
+    wm_gm_int_in_dwi = File(exists=True, mandatory=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    simple = traits.Bool(False, usedefault=True)
+
+
+class _RegisterAtlasDWIOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterAtlasDWI"""
+
+    dwi_aligned_atlas_wmgm_int = File(exists=True, mandatory=True)
+    dwi_aligned_atlas = File(exists=True, mandatory=True)
+    aligned_atlas_t1w = File(exists=True, mandatory=True)
+    node_radius = traits.Any(mandatory=True)
+    atlas = traits.Any(mandatory=False)
+    parcellation4d = traits.Any(mandatory=False)
+    parcellation = traits.Any(mandatory=False)
+    coords = traits.Any(mandatory=True)
+    labels = traits.Any(mandatory=True)
+    wm_in_dwi = File(exists=True, mandatory=True)
+    gm_in_dwi = File(exists=True, mandatory=True)
+    vent_csf_in_dwi = File(exists=True, mandatory=True)
+    B0_mask = File(exists=True, mandatory=True)
+    ap_path = File(exists=True, mandatory=True)
+    gtab_file = File(exists=True, mandatory=True)
+    dwi_file = File(exists=True, mandatory=True)
+    waymask_in_dwi = traits.Any(mandatory=False)
+
+
+class RegisterAtlasDWI(SimpleInterface):
+    """Interface wrapper for RegisterAtlasDWI."""
+
+    input_spec = _RegisterAtlasDWIInputSpec
+    output_spec = _RegisterAtlasDWIOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import time
+        import os
+        import os.path as op
+        from pynets.registration import utils as regutils
+        from pynets.core.nodemaker import \
+            drop_coords_labels_from_restricted_parcellation
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        import pkg_resources
+        from pynets.core import utils
+
+        template = pkg_resources.resource_filename(
+            "pynets", f"templates/{self.inputs.template_name}_brain_"
+                      f"{self.inputs.vox_size}.nii.gz"
+        )
+
+        template_tmp_path = fname_presuffix(
+            template, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            template,
+            template_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        if self.inputs.parcellation is None:
+            parcellation_tmp_path = None
+        else:
+            parcellation_tmp_path = fname_presuffix(
+                self.inputs.parcellation, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.parcellation,
+                parcellation_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+        if self.inputs.parcellation4d is None:
+            parcellation4d_tmp_path = None
+        else:
+            parcellation4d_tmp_path = fname_presuffix(
+                self.inputs.parcellation4d, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.parcellation4d,
+                parcellation4d_tmp_path,
+                copy=True,
+                use_hardlink=False,
+            )
+
+        fa_tmp_path = fname_presuffix(
+            self.inputs.fa_path, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.fa_path,
+            fa_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        ap_tmp_path = fname_presuffix(
+            self.inputs.ap_path, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.ap_path,
+            ap_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        B0_mask_tmp_path = fname_presuffix(
+            self.inputs.B0_mask, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.B0_mask,
+            B0_mask_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        t1w_brain_tmp_path = fname_presuffix(
+            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w_brain,
+            t1w_brain_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        mni2t1w_warp_tmp_path = fname_presuffix(
+            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1w_warp,
+            mni2t1w_warp_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        t1wtissue2dwi_xfm_tmp_path = fname_presuffix(
+            self.inputs.t1wtissue2dwi_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1wtissue2dwi_xfm,
+            t1wtissue2dwi_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        mni2t1_xfm_tmp_path = fname_presuffix(
+            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1_xfm,
+            mni2t1_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        t1w_brain_mask_tmp_path = fname_presuffix(
+            self.inputs.t1w_brain_mask, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w_brain_mask,
+            t1w_brain_mask_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        t1_aligned_mni_tmp_path = fname_presuffix(
+            self.inputs.t1_aligned_mni, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1_aligned_mni,
+            t1_aligned_mni_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        t1w2dwi_bbr_xfm_tmp_path = fname_presuffix(
+            self.inputs.t1w2dwi_bbr_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w2dwi_bbr_xfm,
+            t1w2dwi_bbr_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        t1w2dwi_xfm_tmp_path = fname_presuffix(
+            self.inputs.t1w2dwi_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w2dwi_xfm,
+            t1w2dwi_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        wm_gm_int_in_dwi_tmp_path = fname_presuffix(
+            self.inputs.wm_gm_int_in_dwi, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.wm_gm_int_in_dwi,
+            wm_gm_int_in_dwi_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        if self.inputs.subnet or self.inputs.waymask:
+            if self.inputs.waymask:
+                wm_suf = os.path.basename(self.inputs.waymask).split('.nii')[0]
+                atlas_name = f"{self.inputs.atlas}_" \
+                             f"{wm_suf}"
+            else:
+                atlas_name = f"{self.inputs.atlas}_{self.inputs.subnet}"
+        else:
+            atlas_name = f"{self.inputs.atlas}"
+
+        base_dir_tmp = f"{runtime.cwd}/atlas_{atlas_name}"
+        os.makedirs(base_dir_tmp, exist_ok=True)
+
+        mni2dwi_xfm = f"{base_dir_tmp}{'/'}{atlas_name}" \
+            f"{'_mni2dwi_xfm.mat'}"
+
+        aligned_atlas_t1mni = f"{base_dir_tmp}{'/'}{atlas_name}" \
+                              f"{'_t1w_mni.nii.gz'}"
+        aligned_atlas_skull = f"{base_dir_tmp}{'/'}{atlas_name}" \
+                              f"{'_t1w_skull.nii.gz'}"
+        dwi_aligned_atlas = f"{base_dir_tmp}{'/'}{atlas_name}" \
+                            f"{'_dwi_track.nii.gz'}"
+        dwi_aligned_atlas_wmgm_int = (
+            f"{base_dir_tmp}{'/'}{atlas_name}{'_dwi_track_wmgm_int.nii.gz'}"
+        )
+
+        if self.inputs.node_radius is not None:
+            atlas_name = f"{atlas_name}{'_'}{self.inputs.node_radius}"
+
+        # Apply warps/coregister atlas to dwi
+        [
+            dwi_aligned_atlas_wmgm_int,
+            dwi_aligned_atlas,
+            aligned_atlas_t1w,
+        ] = regutils.atlas2t1w2dwi_align(
+            parcellation_tmp_path,
+            parcellation4d_tmp_path,
+            atlas_name,
+            t1w_brain_tmp_path,
+            t1w_brain_mask_tmp_path,
+            mni2t1w_warp_tmp_path,
+            t1_aligned_mni_tmp_path,
+            ap_tmp_path,
+            mni2t1_xfm_tmp_path,
+            t1wtissue2dwi_xfm_tmp_path,
+            wm_gm_int_in_dwi_tmp_path,
+            aligned_atlas_t1mni,
+            aligned_atlas_skull,
+            dwi_aligned_atlas,
+            dwi_aligned_atlas_wmgm_int,
+            B0_mask_tmp_path,
+            mni2dwi_xfm,
+            self.inputs.simple,
+        )
+
+        # Correct coords and labels
+        [dwi_aligned_atlas, coords, labels] = \
+            drop_coords_labels_from_restricted_parcellation(
+            dwi_aligned_atlas, self.inputs.coords, self.inputs.labels)
+
+        if self.inputs.waymask:
+            waymask_tmp_path = fname_presuffix(
+                self.inputs.waymask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.waymask,
+                waymask_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            # Align waymask
+            waymask_in_t1w = (
+                f"{runtime.cwd}/waymask-"
+                f"{os.path.basename(self.inputs.waymask).split('.nii')[0]}_"
+                f"in_t1w.nii.gz")
+            waymask_in_dwi = (
+                f"{runtime.cwd}/waymask-"
+                f"{os.path.basename(self.inputs.waymask).split('.nii')[0]}_"
+                f"in_dwi.nii.gz")
+
+            waymask_in_dwi = regutils.waymask2dwi_align(
+                waymask_tmp_path,
+                t1w_brain_tmp_path,
+                ap_tmp_path,
+                mni2t1w_warp_tmp_path,
+                mni2t1_xfm_tmp_path,
+                t1wtissue2dwi_xfm_tmp_path,
+                waymask_in_t1w,
+                waymask_in_dwi,
+                B0_mask_tmp_path,
+                template_tmp_path,
+                self.inputs.simple,
+            )
+            time.sleep(0.5)
+            os.system(f"rm -f {waymask_tmp_path} &")
+        else:
+            waymask_in_dwi = None
+
+        if self.inputs.parcellation is None:
+            parcellation_out = self.inputs.parcellation4d
+            copyfile(
+                dwi_aligned_atlas,
+                f"{os.path.dirname(parcellation_out)}/"
+                f"{os.path.basename(dwi_aligned_atlas)}",
+                copy=True,
+                use_hardlink=False,
+            )
+        else:
+            parcellation_out = self.inputs.parcellation
+            copyfile(
+                dwi_aligned_atlas,
+                f"{os.path.dirname(parcellation_out)}/parcellations/"
+                f"{os.path.basename(dwi_aligned_atlas)}",
+                copy=True,
+                use_hardlink=False,
+            )
+
+        # wm_img = nib.load(self.inputs.wm_in_dwi)
+        # wm_data = wm_img.get_fdata().astype('bool')
+        # atlas_img = nib.load(dwi_aligned_atlas)
+        # atlas_data = atlas_img.get_fdata().astype('bool')
+        # B0_mask_img = nib.load(B0_mask_tmp_path)
+        # B0_mask_data = B0_mask_img.get_fdata().astype('bool')
+        # wm_data_dil = binary_opening(wm_data, structure=atlas_data,
+        #                              mask=B0_mask_data)
+        # wm_in_dwi_tmp_path = fname_presuffix(
+        #     self.inputs.wm_in_dwi, suffix="_tmp", newpath=runtime.cwd
+        # )
+        # nib.save(nib.Nifti1Image(wm_data_dil, affine=wm_img.affine,
+        #                          header=wm_img.header), wm_in_dwi_tmp_path)
+        #
+        # wm_img.uncache()
+        # atlas_img.uncache()
+        # B0_mask_img.uncache()
+        # del atlas_data, wm_data, B0_mask_data, wm_data_dil
+        # self._results["wm_in_dwi"] = wm_in_dwi_tmp_path
+
+        self._results["wm_in_dwi"] = self.inputs.wm_in_dwi
+        self._results["dwi_aligned_atlas_wmgm_int"] = \
+            dwi_aligned_atlas_wmgm_int
+        self._results["dwi_aligned_atlas"] = dwi_aligned_atlas
+        self._results["aligned_atlas_t1w"] = aligned_atlas_t1w
+        self._results["node_radius"] = self.inputs.node_radius
+        self._results["atlas"] = self.inputs.atlas
+        self._results["parcellation4d"] = parcellation4d_tmp_path
+        self._results["parcellation"] = parcellation_out
+        self._results["coords"] = coords
+        self._results["labels"] = labels
+        self._results["gm_in_dwi"] = self.inputs.gm_in_dwi
+        self._results["vent_csf_in_dwi"] = self.inputs.vent_csf_in_dwi
+        self._results["B0_mask"] = B0_mask_tmp_path
+        self._results["ap_path"] = ap_tmp_path
+        self._results["gtab_file"] = self.inputs.gtab_file
+        self._results["dwi_file"] = self.inputs.dwi_file
+        self._results["waymask_in_dwi"] = waymask_in_dwi
+
+        dir_path = utils.do_dir_path(
+            self.inputs.atlas, os.path.dirname(self.inputs.dwi_file)
+        )
+
+        namer_dir = "{}/tractography".format(dir_path)
+        if not op.isdir(namer_dir):
+            os.mkdir(namer_dir)
+
+        if not os.path.isfile(f"{namer_dir}/"
+                              f"{op.basename(self.inputs.fa_path)}"):
+            copyfile(
+                self.inputs.fa_path,
+                f"{namer_dir}/{op.basename(self.inputs.fa_path)}",
+                copy=True,
+                use_hardlink=False,
+            )
+        if not os.path.isfile(f"{namer_dir}/"
+                              f"{op.basename(self.inputs.ap_path)}"):
+            copyfile(
+                self.inputs.ap_path,
+                f"{namer_dir}/"
+                f"{op.basename(self.inputs.ap_path).replace('_tmp', '')}",
+                copy=True,
+                use_hardlink=False,
+            )
+        if not os.path.isfile(f"{namer_dir}/"
+                              f"{op.basename(self.inputs.B0_mask)}"):
+            copyfile(
+                self.inputs.B0_mask,
+                f"{namer_dir}/"
+                f"{op.basename(self.inputs.B0_mask).replace('_tmp', '')}",
+                copy=True,
+                use_hardlink=False,
+            )
+        if not os.path.isfile(f"{namer_dir}/"
+                              f"{op.basename(self.inputs.gtab_file)}"):
+            copyfile(
+                self.inputs.gtab_file,
+                f"{namer_dir}/{op.basename(self.inputs.gtab_file)}",
+                copy=True,
+                use_hardlink=False,
+            )
+        if not os.path.isfile(f"{namer_dir}/{op.basename(aligned_atlas_t1w)}"):
+            copyfile(
+                aligned_atlas_t1w,
+                f"{namer_dir}/{op.basename(aligned_atlas_t1w)}",
+                copy=True,
+                use_hardlink=False,
+            )
+
+        reg_tmp = [
+            parcellation_tmp_path,
+            mni2t1w_warp_tmp_path,
+            mni2t1_xfm_tmp_path,
+            t1w_brain_mask_tmp_path,
+            t1_aligned_mni_tmp_path,
+            t1w2dwi_bbr_xfm_tmp_path,
+            t1w2dwi_xfm_tmp_path,
+            t1w_brain_tmp_path
+        ]
+        for j in reg_tmp:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        gc.collect()
+
+        return runtime
+
+
+class _RegisterROIDWIInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterROIDWI"""
+
+    dwi_file = File(exists=True, mandatory=True)
+    basedir_path = Directory(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    roi = traits.Any(mandatory=False)
+    fa_path = File(exists=True, mandatory=True)
+    ap_path = File(exists=True, mandatory=True)
+    B0_mask = File(exists=True, mandatory=True)
+    coords = traits.Any(mandatory=True)
+    labels = traits.Any(mandatory=True)
+    gm_in_dwi = File(exists=True, mandatory=True)
+    vent_csf_in_dwi = File(exists=True, mandatory=True)
+    wm_in_dwi = File(exists=True, mandatory=True)
+    gtab_file = File(exists=True, mandatory=True)
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    t1wtissue2dwi_xfm = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    simple = traits.Bool(False, usedefault=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+
+
+class _RegisterROIDWIOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterROIDWI"""
+
+    roi = traits.Any(mandatory=False)
+
+
+class RegisterROIDWI(SimpleInterface):
+    """Interface wrapper for RegisterROIDWI."""
+
+    input_spec = _RegisterROIDWIInputSpec
+    output_spec = _RegisterROIDWIOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import os
+        import time
+        from pynets.registration import utils as regutils
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        import pkg_resources
+
+        template = pkg_resources.resource_filename(
+            "pynets", f"templates/{self.inputs.template_name}_brain_"
+                      f"{self.inputs.vox_size}.nii.gz"
+        )
+
+        template_tmp_path = fname_presuffix(
+            template, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            template,
+            template_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        ap_tmp_path = fname_presuffix(
+            self.inputs.ap_path, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.ap_path,
+            ap_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        roi_file_tmp_path = fname_presuffix(
+            self.inputs.roi, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.roi,
+            roi_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        t1w_brain_tmp_path = fname_presuffix(
+            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w_brain,
+            t1w_brain_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        mni2t1w_warp_tmp_path = fname_presuffix(
+            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1w_warp,
+            mni2t1w_warp_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        t1wtissue2dwi_xfm_tmp_path = fname_presuffix(
+            self.inputs.t1wtissue2dwi_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1wtissue2dwi_xfm,
+            t1wtissue2dwi_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        mni2t1_xfm_tmp_path = fname_presuffix(
+            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1_xfm,
+            mni2t1_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        roi_in_t1w = f"{runtime.cwd}/waymask-" \
+                     f"{os.path.basename(self.inputs.roi).split('.nii')[0]}" \
+                     f"_in_t1w.nii.gz"
+        roi_in_dwi = f"{runtime.cwd}/waymask-" \
+                     f"{os.path.basename(self.inputs.roi).split('.nii')[0]}" \
+                     f"_in_dwi.nii.gz"
+
+        if self.inputs.roi:
+            t1w_brain_tmp_path2 = fname_presuffix(
+                self.inputs.t1w_brain, suffix="2", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.t1w_brain,
+                t1w_brain_tmp_path2,
+                copy=True,
+                use_hardlink=False)
+
+            mni2t1w_warp_tmp_path2 = fname_presuffix(
+                self.inputs.mni2t1w_warp, suffix="2", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mni2t1w_warp,
+                mni2t1w_warp_tmp_path2,
+                copy=True,
+                use_hardlink=False)
+
+            # Align roi
+            roi_in_dwi = regutils.roi2dwi_align(
+                roi_file_tmp_path,
+                t1w_brain_tmp_path2,
+                roi_in_t1w,
+                roi_in_dwi,
+                mni2t1w_warp_tmp_path2,
+                t1wtissue2dwi_xfm_tmp_path,
+                mni2t1_xfm_tmp_path,
+                template_tmp_path,
+                self.inputs.simple,
+            )
+            time.sleep(0.5)
+        else:
+            roi_in_dwi = None
+
+        self._results["roi"] = roi_in_dwi
+
+        reg_tmp = [
+            t1w_brain_tmp_path,
+            mni2t1w_warp_tmp_path,
+            t1wtissue2dwi_xfm_tmp_path,
+            mni2t1_xfm_tmp_path,
+            template_tmp_path,
+            roi_in_t1w,
+            roi_file_tmp_path
+        ]
+        for j in reg_tmp:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        gc.collect()
+
+        return runtime
+
+
+class _RegisterFuncInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterFunc"""
+
+    anat_file = File(exists=True, mandatory=True)
+    mask = traits.Any(mandatory=False)
+    in_dir = traits.Any(mandatory=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    force_create_mask = traits.Bool(True, usedefault=True)
+    simple = traits.Bool(False, usedefault=True)
+    overwrite = traits.Bool(False, usedefault=True)
+
+
+class _RegisterFuncOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterFunc"""
+
+    reg_fmri_complete = traits.Bool()
+    basedir_path = Directory(exists=True, mandatory=True)
+    t1w_brain_mask = traits.Any(mandatory=False)
+    epi_brain_path = traits.Any()
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    t1_aligned_mni = File(exists=True, mandatory=True)
+    gm_mask = File(exists=True, mandatory=True)
+    t1w2mni_xfm = traits.Any()
+    t1w2mni_warp = traits.Any()
+
+
+class RegisterFunc(SimpleInterface):
+    """Interface wrapper for RegisterFunc to create Func->T1w->MNI mappings."""
+
+    input_spec = _RegisterFuncInputSpec
+    output_spec = _RegisterFuncOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import glob
+        import time
+        import os.path as op
+        from pynets.registration import register
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        from pynets.registration.utils import orient_reslice
+
+        anat_mask_existing = [
+            i
+            for i in glob.glob(self.inputs.in_dir +
+                               "/*_desc-brain_mask.nii.gz")
+            if "MNI" not in i
+        ]
+
+        # Copy T1w mask, if provided, else use existing, if detected, else
+        # compute a fresh one
+        if self.inputs.mask:
+            mask_tmp_path = fname_presuffix(
+                self.inputs.mask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mask,
+                mask_tmp_path,
+                copy=True,
+                use_hardlink=False)
+        else:
+            if len(anat_mask_existing) > 0 and \
+                self.inputs.mask is None and \
+                op.isfile(anat_mask_existing[0]) \
+                    and self.inputs.force_create_mask is False:
+                mask_tmp_path = fname_presuffix(
+                    anat_mask_existing[0], suffix="_tmp",
+                    newpath=runtime.cwd
+                )
+                copyfile(
+                    anat_mask_existing[0],
+                    mask_tmp_path,
+                    copy=True,
+                    use_hardlink=False)
+                mask_tmp_path = orient_reslice(
+                    mask_tmp_path, runtime.cwd, self.inputs.vox_size
+                )
+            else:
+                mask_tmp_path = None
+
+        gm_mask_existing = glob.glob(
+            self.inputs.in_dir +
+            "/*_label-GM_probseg.nii.gz")
+        if len(gm_mask_existing) > 0:
+            gm_mask = fname_presuffix(gm_mask_existing[0],
+                                      newpath=runtime.cwd)
+            copyfile(
+                gm_mask_existing[0],
+                gm_mask,
+                copy=True,
+                use_hardlink=False)
+            gm_mask = orient_reslice(
+                gm_mask, runtime.cwd, self.inputs.vox_size
+            )
+        else:
+            gm_mask = None
+
+        wm_mask_existing = glob.glob(
+            self.inputs.in_dir +
+            "/*_label-WM_probseg.nii.gz")
+        if len(wm_mask_existing) > 0:
+            wm_mask = fname_presuffix(wm_mask_existing[0],
+                                      newpath=runtime.cwd)
+            copyfile(
+                wm_mask_existing[0],
+                wm_mask,
+                copy=True,
+                use_hardlink=False)
+            wm_mask = orient_reslice(
+                wm_mask, runtime.cwd, self.inputs.vox_size
+            )
+        else:
+            wm_mask = None
+
+        anat_file_tmp_path = fname_presuffix(
+            self.inputs.anat_file, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.anat_file,
+            anat_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        reg = register.FmriReg(
+            basedir_path=runtime.cwd,
+            anat_file=anat_file_tmp_path,
+            vox_size=self.inputs.vox_size,
+            template_name=self.inputs.template_name,
+            simple=self.inputs.simple,
+        )
+
+        # Generate T1w brain mask
+        reg.gen_mask(mask_tmp_path)
+
+        # Perform anatomical segmentation
+        reg.gen_tissue(wm_mask, gm_mask, self.inputs.overwrite)
+
+        # Align t1w to mni template
+        # from joblib import Memory
+        # import os
+        # location = f"{outdir}/joblib_" \
+        #            f"{self.inputs.anat_file.split('.nii')[0]}"
+        # os.makedirs(location, exist_ok=True)
+        # memory = Memory(location)
+        # t1w2mni_align = memory.cache(reg.t1w2mni_align)
+        # t1w2mni_align()
+        reg.t1w2mni_align()
+        time.sleep(0.5)
+
+        self._results["reg_fmri_complete"] = True
+        self._results["basedir_path"] = runtime.cwd
+        self._results["t1w_brain_mask"] = reg.t1w_brain_mask
+        self._results["t1w_brain"] = reg.t1w_brain
+        self._results["mni2t1w_warp"] = reg.mni2t1w_warp
+        self._results["mni2t1_xfm"] = reg.mni2t1_xfm
+        self._results["t1_aligned_mni"] = reg.t1_aligned_mni
+        self._results["gm_mask"] = reg.gm_mask
+        self._results["t1w2mni_warp"] = reg.warp_t1w2mni
+        self._results["t1w2mni_xfm"] = reg.t12mni_xfm
+
+        gc.collect()
+
+        return runtime
+
+
+class _RegisterParcellation2MNIFuncInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterParcellation2MNIFunc"""
+
+    parcellation = traits.Any(mandatory=False)
+    t1w2mni_xfm = File(exists=True, mandatory=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    t1w_brain = File(exists=True, mandatory=True)
+    t1w2mni_warp = traits.Any(mandatory=True)
+    dir_path = traits.Any(mandatory=True)
+    simple = traits.Bool(False, usedefault=True)
+
+
+class _RegisterParcellation2MNIFuncOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterParcellation2MNIFunc"""
+
+    aligned_atlas_mni = File(exists=True, mandatory=True)
+
+
+class RegisterParcellation2MNIFunc(SimpleInterface):
+    """Interface wrapper for RegisterParcellation2MNIFunc."""
+
+    input_spec = _RegisterParcellation2MNIFuncInputSpec
+    output_spec = _RegisterParcellation2MNIFuncOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import os
+        import pkg_resources
+        import time
+        from pynets.core.utils import prune_suffices
+        from pynets.registration import utils as regutils
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+
+        template = pkg_resources.resource_filename(
+            "pynets", f"templates/{self.inputs.template_name}_brain_"
+                      f"{self.inputs.vox_size}.nii.gz"
+        )
+        template_mask = pkg_resources.resource_filename(
+            "pynets", f"templates/{self.inputs.template_name}_"
+                      f"brain_mask_{self.inputs.vox_size}.nii.gz"
+        )
+
+        template_tmp_path = fname_presuffix(
+            template, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            template,
+            template_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        template_mask_tmp_path = fname_presuffix(
+            template_mask, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            template_mask,
+            template_mask_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        t1w_brain_tmp_path = fname_presuffix(
+            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w_brain,
+            t1w_brain_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        parcellation_tmp_path = fname_presuffix(
+            self.inputs.parcellation, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.parcellation,
+            parcellation_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        atlas_name = prune_suffices(os.path.basename(self.inputs.parcellation
+                                                     ).split('.nii')[0])
+
+        base_dir_tmp = f"{runtime.cwd}/atlas_{atlas_name}"
+
+        os.makedirs(base_dir_tmp, exist_ok=True)
+
+        aligned_atlas_t1w = f"{base_dir_tmp}{'/'}{atlas_name}_" \
+                            f"t1w_res.nii.gz"
+
+        aligned_atlas_mni = f"{base_dir_tmp}{'/'}{atlas_name}_" \
+                            f"t1w_mni.nii.gz"
+
+        t1w2mni_xfm_tmp_path = fname_presuffix(
+            self.inputs.t1w2mni_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w2mni_xfm,
+            t1w2mni_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        t1w2mni_warp_tmp_path = fname_presuffix(
+            self.inputs.t1w2mni_warp, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w2mni_warp,
+            t1w2mni_warp_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        aligned_atlas_mni = regutils.RegisterParcellation2MNIFunc_align(
+            parcellation_tmp_path,
+            template_tmp_path,
+            template_mask_tmp_path,
+            t1w_brain_tmp_path,
+            t1w2mni_xfm_tmp_path,
+            aligned_atlas_t1w,
+            aligned_atlas_mni,
+            t1w2mni_warp_tmp_path,
+            self.inputs.simple
+        )
+        time.sleep(0.5)
+
+        out_dir = f"{self.inputs.dir_path}/t1w_clustered_parcellations/"
+        os.makedirs(out_dir, exist_ok=True)
+        copyfile(
+            aligned_atlas_t1w,
+            f"{out_dir}{atlas_name}_t1w_skull.nii.gz",
+            copy=True,
+            use_hardlink=False)
+
+        self._results["aligned_atlas_mni"] = aligned_atlas_mni
+
+        reg_tmp = [
+            parcellation_tmp_path,
+            template_tmp_path,
+            t1w2mni_xfm_tmp_path,
+            t1w2mni_warp_tmp_path,
+            template_mask_tmp_path
+        ]
+
+        for j in reg_tmp:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+
+        gc.collect()
+
+        return runtime
+
+
+class _RegisterAtlasFuncInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterAtlasFunc"""
+
+    atlas = traits.Any(mandatory=False)
+    subnet = traits.Any(mandatory=True)
+    parcellation4d = traits.Any(mandatory=True)
+    parcellation = traits.Any(mandatory=False)
+    basedir_path = Directory(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    t1w_brain_mask = File(exists=True, mandatory=True)
+    t1_aligned_mni = File(exists=True, mandatory=True)
+    gm_mask = File(exists=True, mandatory=True)
+    coords = traits.Any(mandatory=True)
+    labels = traits.Any(mandatory=True)
+    node_radius = traits.Any(mandatory=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    reg_fmri_complete = traits.Bool()
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    already_run = traits.Bool(False, usedefault=True)
+    dir_path = traits.Any(mandatory=True)
+    simple = traits.Bool(False, usedefault=True)
+
+
+class _RegisterAtlasFuncOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterAtlasFunc"""
+
+    aligned_atlas_gm = File(exists=True, mandatory=True)
+    coords = traits.Any(mandatory=True)
+    labels = traits.Any(mandatory=True)
+    node_radius = traits.Any()
+    atlas = traits.Any()
+
+
+class RegisterAtlasFunc(SimpleInterface):
+    """Interface wrapper for RegisterAtlasFunc."""
+
+    input_spec = _RegisterAtlasFuncInputSpec
+    output_spec = _RegisterAtlasFuncOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import os
+        import time
+        import glob
+        from pynets.registration import utils as regutils
+        from pynets.core.nodemaker import \
+            drop_coords_labels_from_restricted_parcellation
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+
+        if self.inputs.subnet:
+            atlas_name = f"{self.inputs.atlas}_{self.inputs.subnet}"
+        else:
+            atlas_name = f"{self.inputs.atlas}"
+
+        if self.inputs.already_run is True:
+            try:
+                aligned_atlas_gm = [i for i in
+                                    glob.glob(f"{self.inputs.dir_path}/"
+                                              f"t1w_clustered_parcellations/"
+                                              f"*.nii.gz") if
+                                    atlas_name.strip('_mni') in i][0]
+
+                # Correct coords and labels
+                [aligned_atlas_gm, coords, labels] = \
+                    drop_coords_labels_from_restricted_parcellation(
+                        aligned_atlas_gm, self.inputs.coords,
+                        self.inputs.labels)
+
+            except FileNotFoundError as e:
+                print(e, 'T1w-space parcellation not found. Did you delete '
+                      'outputs?')
+        else:
+            if self.inputs.parcellation is None:
+                parcellation_tmp_path = None
+            else:
+                parcellation_tmp_path = fname_presuffix(
+                    self.inputs.parcellation, suffix="_tmp", newpath=runtime.cwd
+                )
+                copyfile(
+                    self.inputs.parcellation,
+                    parcellation_tmp_path,
+                    copy=True,
+                    use_hardlink=False)
+
+            if self.inputs.parcellation4d is None:
+                parcellation4d_tmp_path = None
+            else:
+                parcellation4d_tmp_path = fname_presuffix(
+                    self.inputs.parcellation4d, suffix="_tmp",
+                    newpath=runtime.cwd
+                )
+                copyfile(
+                    self.inputs.parcellation4d,
+                    parcellation4d_tmp_path,
+                    copy=True,
+                    use_hardlink=False,
+                )
+
+            t1w_brain_tmp_path = fname_presuffix(
+                self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.t1w_brain,
+                t1w_brain_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            t1w_brain_mask_tmp_path = fname_presuffix(
+                self.inputs.t1w_brain_mask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.t1w_brain_mask,
+                t1w_brain_mask_tmp_path,
+                copy=True,
+                use_hardlink=False,
+            )
+
+            t1_aligned_mni_tmp_path = fname_presuffix(
+                self.inputs.t1_aligned_mni, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.t1_aligned_mni,
+                t1_aligned_mni_tmp_path,
+                copy=True,
+                use_hardlink=False,
+            )
+
+            gm_mask_tmp_path = fname_presuffix(
+                self.inputs.gm_mask, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.gm_mask,
+                gm_mask_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            base_dir_tmp = f"{runtime.cwd}/atlas_{atlas_name}"
+            os.makedirs(base_dir_tmp, exist_ok=True)
+
+            aligned_atlas_t1mni = f"{base_dir_tmp}{'/'}{atlas_name}_" \
+                                  f"t1w_mni.nii.gz"
+            aligned_atlas_skull = f"{base_dir_tmp}{'/'}{atlas_name}_" \
+                                  f"t1w_skull.nii.gz"
+            aligned_atlas_gm = f"{base_dir_tmp}{'/'}{atlas_name}{'_gm.nii.gz'}"
+
+            if self.inputs.node_radius is not None:
+                atlas_name = f"{atlas_name}{'_'}{self.inputs.node_radius}"
+
+            mni2t1_xfm_tmp_path = fname_presuffix(
+                self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mni2t1_xfm,
+                mni2t1_xfm_tmp_path,
+                copy=True,
+                use_hardlink=False)
+
+            mni2t1w_warp_tmp_path = fname_presuffix(
+                self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
+            )
+            copyfile(
+                self.inputs.mni2t1w_warp,
+                mni2t1w_warp_tmp_path,
+                copy=True,
+                use_hardlink=False,
+            )
+
+            aligned_atlas_gm, aligned_atlas_skull = regutils.atlas2t1w_align(
+                parcellation_tmp_path,
+                parcellation4d_tmp_path,
+                atlas_name,
+                t1w_brain_tmp_path,
+                t1w_brain_mask_tmp_path,
+                t1_aligned_mni_tmp_path,
+                mni2t1w_warp_tmp_path,
+                mni2t1_xfm_tmp_path,
+                gm_mask_tmp_path,
+                aligned_atlas_t1mni,
+                aligned_atlas_skull,
+                aligned_atlas_gm,
+                self.inputs.simple,
+            )
+            time.sleep(0.5)
+
+            # Correct coords and labels
+            [aligned_atlas_gm, coords, labels] = \
+                drop_coords_labels_from_restricted_parcellation(
+                aligned_atlas_gm, self.inputs.coords, self.inputs.labels)
+
+            reg_tmp = [
+                parcellation4d_tmp_path,
+                parcellation_tmp_path,
+                gm_mask_tmp_path,
+                t1_aligned_mni_tmp_path,
+                t1w_brain_mask_tmp_path,
+                mni2t1w_warp_tmp_path,
+                mni2t1_xfm_tmp_path,
+            ]
+
+            if self.inputs.parcellation is None:
+                parcellation_out = self.inputs.parcellation4d
+                copyfile(
+                    aligned_atlas_gm,
+                    f"{os.path.dirname(parcellation_out)}/"
+                    f"{os.path.basename(aligned_atlas_gm)}",
+                    copy=True,
+                    use_hardlink=False,
+                )
+            else:
+                parcellation_out = self.inputs.parcellation
+                copyfile(
+                    aligned_atlas_gm,
+                    f"{os.path.dirname(parcellation_out)}/parcellations/"
+                    f"{os.path.basename(aligned_atlas_gm)}",
+                    copy=True,
+                    use_hardlink=False,
+                )
+
+            for j in reg_tmp:
+                if j is not None:
+                    if os.path.isfile(j):
+                        os.system(f"rm -f {j} &")
+
+        # Use for debugging check
+        parcellation_img = nib.load(aligned_atlas_gm)
+
+        intensities = [i for i in list(np.unique(
+            np.asarray(
+                parcellation_img.dataobj).astype("int"))
+        ) if i != 0]
+        try:
+            assert len(coords) == len(labels) == len(intensities)
+        except ValueError as e:
+            print(e, 'Failed!')
+            print(f"# Coords: {len(coords)}")
+            print(f"# Labels: {len(labels)}")
+            print(f"# Intensities: {len(intensities)}")
+
+        self._results["aligned_atlas_gm"] = aligned_atlas_gm
+        self._results["coords"] = coords
+        self._results["labels"] = labels
+        self._results["atlas"] = self.inputs.atlas
+        self._results["node_radius"] = self.inputs.node_radius
+
+        gc.collect()
+
+        return runtime
+
+
+class _RegisterROIEPIInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for RegisterROIEPI"""
+
+    basedir_path = Directory(exists=True, mandatory=True)
+    anat_file = File(exists=True, mandatory=True)
+    vox_size = traits.Str("2mm", mandatory=True, usedefault=True)
+    roi = traits.Any(mandatory=False)
+    t1w_brain = File(exists=True, mandatory=True)
+    mni2t1w_warp = File(exists=True, mandatory=True)
+    mni2t1_xfm = File(exists=True, mandatory=True)
+    template_name = traits.Str("MNI152_T1", mandatory=True, usedefault=True)
+    simple = traits.Bool(False, usedefault=True)
+
+
+class _RegisterROIEPIOutputSpec(TraitedSpec):
+    """Output interface wrapper for RegisterROIEPI"""
+
+    roi = traits.Any(mandatory=False)
+
+
+class RegisterROIEPI(SimpleInterface):
+    """Interface wrapper for RegisterROIEPI."""
+
+    input_spec = _RegisterROIEPIInputSpec
+    output_spec = _RegisterROIEPIOutputSpec
+
+    def _run_interface(self, runtime):
+        import gc
+        import os
+        import time
+        from pynets.registration import utils as regutils
+        from nipype.utils.filemanip import fname_presuffix, copyfile
+        import pkg_resources
+
+        template = pkg_resources.resource_filename(
+            "pynets", f"templates/{self.inputs.template_name}_brain_"
+                      f"{self.inputs.vox_size}.nii.gz"
+        )
+        template_tmp_path = fname_presuffix(
+            template, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            template,
+            template_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        roi_file_tmp_path = fname_presuffix(
+            self.inputs.roi, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.roi,
+            roi_file_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        roi_in_t1w = f"{runtime.cwd}/roi-" \
+                     f"{os.path.basename(self.inputs.roi).split('.nii')[0]}" \
+                     f"_in_t1w.nii.gz"
+
+        t1w_brain_tmp_path = fname_presuffix(
+            self.inputs.t1w_brain, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.t1w_brain,
+            t1w_brain_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        mni2t1w_warp_tmp_path = fname_presuffix(
+            self.inputs.mni2t1w_warp, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1w_warp,
+            mni2t1w_warp_tmp_path,
+            copy=True,
+            use_hardlink=False,
+        )
+
+        mni2t1_xfm_tmp_path = fname_presuffix(
+            self.inputs.mni2t1_xfm, suffix="_tmp", newpath=runtime.cwd
+        )
+        copyfile(
+            self.inputs.mni2t1_xfm,
+            mni2t1_xfm_tmp_path,
+            copy=True,
+            use_hardlink=False)
+
+        if self.inputs.roi:
+            # Align roi
+            roi_in_t1w = regutils.roi2t1w_align(
+                roi_file_tmp_path,
+                t1w_brain_tmp_path,
+                mni2t1_xfm_tmp_path,
+                mni2t1w_warp_tmp_path,
+                roi_in_t1w,
+                template_tmp_path,
+                self.inputs.simple,
+            )
+            time.sleep(0.5)
+        else:
+            roi_in_t1w = None
+
+        self._results["roi"] = roi_in_t1w
+
+        reg_tmp = [
+            t1w_brain_tmp_path,
+            mni2t1w_warp_tmp_path,
+            mni2t1_xfm_tmp_path,
+            roi_file_tmp_path,
+            template_tmp_path
+        ]
+        for j in reg_tmp:
+            if j is not None:
+                if os.path.isfile(j):
+                    os.system(f"rm -f {j} &")
+        gc.collect()
+
+        return runtime
+
diff --git a/pynets/registration/register.py b/pynets/registration/register.py
index eb96d7e1..1bf66ef0 100755
--- a/pynets/registration/register.py
+++ b/pynets/registration/register.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner
 """
 import os
 import nibabel as nib
diff --git a/pynets/registration/utils.py b/pynets/registration/utils.py
index 1349408e..c73cbfe0 100644
--- a/pynets/registration/utils.py
+++ b/pynets/registration/utils.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@author: Derek Pisner
 """
 import os
 import numpy as np
@@ -22,9 +21,7 @@
 
 
 def gen_mask(t1w_head, t1w_brain, mask):
-    import time
     import os.path as op
-    from pynets.registration import utils as regutils
     from nilearn.image import math_img
 
     t1w_brain_mask = f"{op.dirname(t1w_head)}/t1w_brain_mask.nii.gz"
@@ -56,7 +53,7 @@ def gen_mask(t1w_head, t1w_brain, mask):
     math_img("img > 0.0001", img=nib.load(t1w_brain_mask)
              ).to_filename(t1w_brain_mask)
 
-    t1w_brain = regutils.apply_mask_to_image(t1w_head, t1w_brain_mask,
+    t1w_brain = apply_mask_to_image(t1w_head, t1w_brain_mask,
                                              t1w_brain)
 
     assert op.isfile(t1w_brain)
@@ -118,7 +115,6 @@ def atlas2t1w2dwi_align(
     import gc
     from nilearn.image import resample_to_img
     from pynets.core.utils import checkConsecutive
-    from pynets.registration import utils as regutils
     from nilearn.image import math_img
     from nilearn.masking import intersect_masks
 
@@ -143,7 +139,7 @@ def atlas2t1w2dwi_align(
 
     if simple is False:
         try:
-            regutils.apply_warp(
+            apply_warp(
                 t1w_brain,
                 aligned_atlas_t1mni,
                 aligned_atlas_skull,
@@ -155,7 +151,7 @@ def atlas2t1w2dwi_align(
             time.sleep(0.5)
 
             # Apply linear transformation from template to dwi space
-            regutils.applyxfm(ap_path, aligned_atlas_skull, t1w2dwi_xfm,
+            applyxfm(ap_path, aligned_atlas_skull, t1w2dwi_xfm,
                               dwi_aligned_atlas, interp="nearestneighbour")
             time.sleep(0.5)
         except BaseException:
@@ -163,21 +159,21 @@ def atlas2t1w2dwi_align(
                 "Warning: Atlas is not in correct dimensions, or input is low"
                 " quality,\nusing linear template registration.")
 
-            regutils.applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
+            applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
                               aligned_atlas_skull, interp="nearestneighbour")
             time.sleep(0.5)
             combine_xfms(mni2t1_xfm, t1w2dwi_xfm, mni2dwi_xfm)
             time.sleep(0.5)
-            regutils.applyxfm(ap_path, aligned_atlas_t1mni, mni2dwi_xfm,
+            applyxfm(ap_path, aligned_atlas_t1mni, mni2dwi_xfm,
                               dwi_aligned_atlas, interp="nearestneighbour")
             time.sleep(0.5)
     else:
-        regutils.applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
+        applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
                           aligned_atlas_skull, interp="nearestneighbour")
         time.sleep(0.5)
         combine_xfms(mni2t1_xfm, t1w2dwi_xfm, mni2dwi_xfm)
         time.sleep(0.5)
-        regutils.applyxfm(ap_path, aligned_atlas_t1mni, mni2dwi_xfm,
+        applyxfm(ap_path, aligned_atlas_t1mni, mni2dwi_xfm,
                           dwi_aligned_atlas, interp="nearestneighbour")
         time.sleep(0.5)
 
@@ -201,15 +197,15 @@ def atlas2t1w2dwi_align(
     nib.save(atlas_img_corr, dwi_aligned_atlas)
     nib.save(dwi_aligned_atlas_wmgm_int_img, dwi_aligned_atlas_wmgm_int)
 
-    dwi_aligned_atlas = regutils.apply_mask_to_image(dwi_aligned_atlas,
+    dwi_aligned_atlas = apply_mask_to_image(dwi_aligned_atlas,
                                                      B0_mask,
                                                      dwi_aligned_atlas)
 
-    dwi_aligned_atlas_wmgm_int = regutils.apply_mask_to_image(
+    dwi_aligned_atlas_wmgm_int = apply_mask_to_image(
         dwi_aligned_atlas_wmgm_int, B0_mask, dwi_aligned_atlas_wmgm_int)
 
-    final_dat = atlas_img_corr.get_fdata()
-    unique_a = sorted(set(np.array(final_dat.flatten().tolist())))
+    unique_a = sorted(set(np.array(
+        np.ndarray.flatten(np.asarray(atlas_img_corr.dataobj)).tolist())))
 
     if not checkConsecutive(unique_a):
         print("Warning! Non-consecutive integers found in parcellation...")
@@ -248,7 +244,6 @@ def roi2dwi_align(
     MNI space --> T1w --> dwi.
     """
     import time
-    from pynets.registration import utils as regutils
     from nilearn.image import resample_to_img
 
     roi_img = nib.load(roi)
@@ -263,13 +258,13 @@ def roi2dwi_align(
     # Apply warp or transformer resulting from the inverse MNI->T1w created
     # earlier
     if simple is False:
-        regutils.apply_warp(t1w_brain, roi, roi_in_t1w, warp=mni2t1w_warp)
+        apply_warp(t1w_brain, roi, roi_in_t1w, warp=mni2t1w_warp)
     else:
-        regutils.applyxfm(t1w_brain, roi, mni2t1_xfm, roi_in_t1w)
+        applyxfm(t1w_brain, roi, mni2t1_xfm, roi_in_t1w)
 
     time.sleep(0.5)
     # Apply transform from t1w to native dwi space
-    regutils.applyxfm(ap_path, roi_in_t1w, t1wtissue2dwi_xfm, roi_in_dwi)
+    applyxfm(ap_path, roi_in_t1w, t1wtissue2dwi_xfm, roi_in_dwi)
 
     return roi_in_dwi
 
@@ -292,7 +287,6 @@ def waymask2dwi_align(
     MNI space --> T1w --> dwi.
     """
     import time
-    from pynets.registration import utils as regutils
     from nilearn.image import resample_to_img
 
     # Apply warp or transformer resulting from the inverse MNI->T1w created
@@ -307,18 +301,18 @@ def waymask2dwi_align(
     nib.save(waymask_img_res, waymask_res)
 
     if simple is False:
-        regutils.apply_warp(
+        apply_warp(
             t1w_brain,
             waymask_res,
             waymask_in_t1w,
             warp=mni2t1w_warp)
     else:
-        regutils.applyxfm(t1w_brain, waymask_res, mni2t1_xfm,
+        applyxfm(t1w_brain, waymask_res, mni2t1_xfm,
                           waymask_in_t1w)
 
     time.sleep(0.5)
     # Apply transform from t1w to native dwi space
-    regutils.applyxfm(
+    applyxfm(
         ap_path,
         waymask_in_t1w,
         t1wtissue2dwi_xfm,
@@ -326,7 +320,7 @@ def waymask2dwi_align(
 
     time.sleep(0.5)
 
-    waymask_in_dwi = regutils.apply_mask_to_image(waymask_in_dwi,
+    waymask_in_dwi = apply_mask_to_image(waymask_in_dwi,
                                                   B0_mask_tmp_path,
                                                   waymask_in_dwi)
 
@@ -345,7 +339,6 @@ def roi2t1w_align(
     A function to perform alignment of a roi from MNI space --> T1w.
     """
     import time
-    from pynets.registration import utils as regutils
     from nilearn.image import resample_to_img
 
     roi_img = nib.load(roi)
@@ -360,10 +353,10 @@ def roi2t1w_align(
     # Apply warp or transformer resulting from the inverse
     # MNI->T1w created earlier
     if simple is False:
-        regutils.apply_warp(t1w_brain, roi_res, roi_in_t1w,
+        apply_warp(t1w_brain, roi_res, roi_in_t1w,
                             warp=mni2t1w_warp)
     else:
-        regutils.applyxfm(t1w_brain, roi_res, mni2t1_xfm,
+        applyxfm(t1w_brain, roi_res, mni2t1_xfm,
                           roi_in_t1w)
 
     time.sleep(0.5)
@@ -386,7 +379,6 @@ def RegisterParcellation2MNIFunc_align(
     A function to perform atlas alignment from T1w atlas --> MNI.
     """
     import time
-    from pynets.registration import utils as regutils
     from nilearn.image import resample_to_img
 
     atlas_img = nib.load(parcellation)
@@ -405,7 +397,7 @@ def RegisterParcellation2MNIFunc_align(
 
     if simple is False:
         try:
-            regutils.apply_warp(
+            apply_warp(
                 template,
                 aligned_atlas_t1w,
                 aligned_atlas_mni,
@@ -419,7 +411,7 @@ def RegisterParcellation2MNIFunc_align(
                 "Warning: Atlas is not in correct dimensions, or input is "
                 "low quality,\nusing linear template registration.")
 
-            regutils.align(
+            align(
                 aligned_atlas_t1w,
                 template,
                 init=t1w2mni_xfm,
@@ -431,7 +423,7 @@ def RegisterParcellation2MNIFunc_align(
             )
             time.sleep(0.5)
     else:
-        regutils.align(
+        align(
             aligned_atlas_t1w,
             template,
             init=t1w2mni_xfm,
@@ -465,7 +457,6 @@ def atlas2t1w_align(
     A function to perform atlas alignment from atlas --> T1w.
     """
     import time
-    from pynets.registration import utils as regutils
     from nilearn.image import resample_to_img
     # from pynets.core.utils import checkConsecutive
 
@@ -490,7 +481,7 @@ def atlas2t1w_align(
 
     if simple is False:
         try:
-            regutils.apply_warp(
+            apply_warp(
                 t1w_brain,
                 aligned_atlas_t1mni,
                 aligned_atlas_skull,
@@ -505,18 +496,18 @@ def atlas2t1w_align(
                 "Warning: Atlas is not in correct dimensions, or input is low "
                 "quality,\nusing linear template registration.")
 
-            regutils.applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
+            applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
                               aligned_atlas_skull, interp="nearestneighbour")
             time.sleep(0.5)
     else:
-        regutils.applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
+        applyxfm(t1w_brain, aligned_atlas_t1mni, mni2t1_xfm,
                           aligned_atlas_skull, interp="nearestneighbour")
         time.sleep(0.5)
 
-    # aligned_atlas_gm = regutils.apply_mask_to_image(aligned_atlas_skull,
+    # aligned_atlas_gm = apply_mask_to_image(aligned_atlas_skull,
     #                                                 gm_mask,
     #                                                 aligned_atlas_gm)
-    aligned_atlas_gm = regutils.apply_mask_to_image(aligned_atlas_skull,
+    aligned_atlas_gm = apply_mask_to_image(aligned_atlas_skull,
                                                     t1w_brain_mask,
                                                     aligned_atlas_gm)
 
@@ -541,7 +532,7 @@ def atlas2t1w_align(
     # if diff > gm_fail_tol:
     #     print(f"Grey-Matter mask too restrictive >{str(gm_fail_tol)} for this "
     #           f"parcellation. Falling back to the T1w mask...")
-    #     aligned_atlas_gm = regutils.apply_mask_to_image(aligned_atlas_skull,
+    #     aligned_atlas_gm = apply_mask_to_image(aligned_atlas_skull,
     #                                                     t1w_brain_mask,
     #                                                     aligned_atlas_gm)
     #     time.sleep(5)
@@ -1182,11 +1173,8 @@ def orient_reslice(
         is a dwi.
 
     """
-    from pynets.registration.utils import (
-        reorient_dwi,
-        reorient_img,
-        match_target_vox_res,
-    )
+    from pynets.registration.utils import reorient_img, reorient_dwi, \
+        match_target_vox_res
     import time
 
     img = nib.load(infile)
@@ -1199,13 +1187,13 @@ def orient_reslice(
         if ("reor-RAS" not in infile) or (overwrite is True):
             [infile, bvecs] = reorient_dwi(
                 infile, bvecs, outdir, overwrite=overwrite)
-            time.sleep(0.5)
+            time.sleep(0.25)
         # Check dimensions
         if ("res-" not in infile) or (overwrite is True):
             outfile = match_target_vox_res(
                 infile, vox_size, outdir, overwrite=overwrite
             )
-            time.sleep(0.5)
+            time.sleep(0.25)
             print(outfile)
         else:
             outfile = infile
@@ -1214,13 +1202,13 @@ def orient_reslice(
         # Check orientation
         if ("reor-RAS" not in infile) or (overwrite is True):
             infile = reorient_img(infile, outdir, overwrite=overwrite)
-            time.sleep(0.5)
+            time.sleep(0.25)
         # Check dimensions
         if ("res-" not in infile) or (overwrite is True):
             outfile = match_target_vox_res(
                 infile, vox_size, outdir, overwrite=overwrite
             )
-            time.sleep(0.5)
+            time.sleep(0.25)
             print(outfile)
         else:
             outfile = infile
@@ -1229,13 +1217,13 @@ def orient_reslice(
         # Check orientation
         if ("reor-RAS" not in infile) or (overwrite is True):
             infile = reorient_img(infile, outdir, overwrite=overwrite)
-            time.sleep(0.5)
+            time.sleep(0.25)
         # Check dimensions
         if ("res-" not in infile) or (overwrite is True):
             outfile = match_target_vox_res(
                 infile, vox_size, outdir, overwrite=overwrite
             )
-            time.sleep(0.5)
+            time.sleep(0.25)
             print(outfile)
         else:
             outfile = infile
@@ -1310,7 +1298,6 @@ def reorient_dwi(dwi_prep, bvecs, out_dir, overwrite=True):
 
     """
     import os
-    from pynets.registration.utils import normalize_xform
 
     fname = dwi_prep
     bvec_fname = bvecs
@@ -1397,8 +1384,6 @@ def reorient_img(img, out_dir, overwrite=True):
         File path to reoriented Nifti1Image.
 
     """
-    from pynets.registration.utils import normalize_xform
-
     # Load image, orient as RAS
     orig_img = nib.load(img)
     normalized = normalize_xform(nib.as_closest_canonical(orig_img))
@@ -1427,8 +1412,8 @@ def reorient_img(img, out_dir, overwrite=True):
     return out_name
 
 
-def match_target_vox_res(img_file, vox_size, out_dir,
-                         overwrite=True, remove_orig=True):
+def match_target_vox_res(img_file, vox_size, out_dir, overwrite=True,
+                         remove_orig=True):
     """
     A function to resample an image to a given isotropic voxel
     resolution.
diff --git a/pynets/stats/__init__.py b/pynets/statistics/__init__.py
similarity index 80%
rename from pynets/stats/__init__.py
rename to pynets/statistics/__init__.py
index 96091956..84df7ff8 100644
--- a/pynets/stats/__init__.py
+++ b/pynets/statistics/__init__.py
@@ -3,7 +3,7 @@
 __author__ = """Derek Pisner"""
 __email__ = "dpisner@utexas.edu"
 
-from . import *
+from .individual import multiplex
 import warnings
 
 warnings.filterwarnings("ignore")
diff --git a/pynets/statistics/group/__init__.py b/pynets/statistics/group/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pynets/stats/benchmarking.py b/pynets/statistics/group/benchmarking.py
similarity index 99%
rename from pynets/stats/benchmarking.py
rename to pynets/statistics/group/benchmarking.py
index e91bba00..88a2591f 100644
--- a/pynets/stats/benchmarking.py
+++ b/pynets/statistics/group/benchmarking.py
@@ -3,7 +3,6 @@
 """
 Created on Tue Nov  7 10:40:07 2017
 Copyright (C) 2017
-@authors: Derek Pisner
 """
 import os
 from sklearn.metrics.pairwise import (
@@ -19,7 +18,6 @@
 import numpy as np
 import warnings
 from sklearn.preprocessing import StandardScaler
-from pynets.stats.utils import flatten_latent_positions
 from pynets.core.utils import flatten
 warnings.filterwarnings("ignore")
 
@@ -203,7 +201,7 @@ def benchmark_reproducibility(base_dir, comb, modality, alg, par_dict, disc,
     from pathlib import Path
     import ast
     import matplotlib
-    from pynets.stats.utils import gen_sub_vec
+    from pynets.statistics.utils import gen_sub_vec
     matplotlib.use('Agg')
 
     df_summary = pd.DataFrame(
@@ -239,7 +237,7 @@ def benchmark_reproducibility(base_dir, comb, modality, alg, par_dict, disc,
 
     # icc
     if icc is True:
-        from pynets.stats.utils import parse_closest_ixs
+        from pynets.statistics.utils import parse_closest_ixs
         try:
             import pingouin as pg
         except ImportError:
diff --git a/pynets/stats/prediction.py b/pynets/statistics/group/prediction.py
similarity index 99%
rename from pynets/stats/prediction.py
rename to pynets/statistics/group/prediction.py
index 9423ac99..c75da4f4 100644
--- a/pynets/stats/prediction.py
+++ b/pynets/statistics/group/prediction.py
@@ -2,7 +2,6 @@
 # -*- coding: utf-8 -*-
 """
 Copyright (C) 2017
-@authors: Derek Pisner
 """
 import pandas as pd
 import os
@@ -26,7 +25,6 @@
 from operator import itemgetter
 from sklearn.preprocessing import StandardScaler, MinMaxScaler
 from pynets.core.utils import flatten
-from sklearn.svm import LinearSVC
 from sklearn.base import BaseEstimator, TransformerMixin, clone, \
     ClassifierMixin
 from sklearn.dummy import DummyClassifier, DummyRegressor
@@ -1648,7 +1646,6 @@ def bootstrapped_nested_cv(
 
 def make_x_y(input_dict, drop_cols, target_var, embedding_type, grid_param):
     import pandas as pd
-    from time import sleep
     import json
 
     print(target_var)
@@ -1859,11 +1856,8 @@ class MakeXY(SimpleInterface):
     output_spec = _MakeXYOutputSpec
 
     def _run_interface(self, runtime):
-        import gc
         import os
-        import time
         from ast import literal_eval
-        from pynets.stats.prediction import make_x_y
 
         self._results["embedding_type"] = self.inputs.embedding_type
         self._results["grid_param"] = self.inputs.grid_param
@@ -1998,7 +1992,6 @@ class BSNestedCV(SimpleInterface):
 
     def _run_interface(self, runtime):
         import gc
-        import os
         from colorama import Fore, Style
         from joblib import dump
 
@@ -2166,11 +2159,9 @@ class MakeDF(SimpleInterface):
 
     def _run_interface(self, runtime):
         import gc
-        import os
         from ast import literal_eval
         import pandas as pd
         import numpy as np
-        from colorama import Fore, Style
 
         def get_CI(stats, alpha=0.95):
             p = ((1.0 - alpha) / 2.0) * 100
@@ -2297,8 +2288,6 @@ def get_CI(stats, alpha=0.95):
 
 def create_wf(grid_params_mod, basedir, n_boots, nuisance_cols, dummy_run,
               search_method, stack, stack_prefix_list):
-    from pynets.stats.prediction import MakeXY, MakeDF, BSNestedCV, \
-        concatenate_frames
     from nipype.pipeline import engine as pe
     from nipype.interfaces import utility as niu
     import uuid
@@ -2474,7 +2463,6 @@ def create_wf(grid_params_mod, basedir, n_boots, nuisance_cols, dummy_run,
 
 
 def build_predict_workflow(args, retval, verbose=True):
-    from pynets.stats.prediction import create_wf
     from nipype.pipeline import engine as pe
     from nipype.interfaces import utility as niu
     import uuid
diff --git a/pynets/statistics/individual/__init__.py b/pynets/statistics/individual/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pynets/stats/netstats.py b/pynets/statistics/individual/algorithms.py
similarity index 99%
rename from pynets/stats/netstats.py
rename to pynets/statistics/individual/algorithms.py
index f66bfd5a..590911c7 100644
--- a/pynets/stats/netstats.py
+++ b/pynets/statistics/individual/algorithms.py
@@ -21,7 +21,7 @@
     DEFAULT_ENGINE = hardcoded_params["graph_analysis_engine"][0]
 except FileNotFoundError as e:
     import sys
-    print(e, "Failed to parse runconfig.yaml")
+    print(e, "Failed to parse advanced.yaml")
 
 
 def get_prop_type(value, key=None):
@@ -1415,7 +1415,6 @@ def normalize_graph(self):
         return self.G
 
     def prune_graph(self, remove_self_loops=True):
-        import os
         from pynets.core import utils
         from graspologic.utils import largest_connected_component, \
             remove_loops, symmetrize
@@ -1442,7 +1441,7 @@ def prune_graph(self, remove_self_loops=True):
                                              method=hub_detection_method)
             except FileNotFoundError as e:
                 import sys
-                print(e, "Failed to parse runconfig.yaml")
+                print(e, "Failed to parse advanced.yaml")
 
         elif int(self.prune) == 3:
             print("Pruning all but the largest connected "
@@ -2029,13 +2028,12 @@ def extractnetstats(
     # import random
     import pkg_resources
     import networkx
-    from pynets.stats import netstats
     from pathlib import Path
 
     # Load netstats config and parse graph algorithms as objects
     with open(
         pkg_resources.resource_filename("pynets",
-                                        "stats/global.yaml"),
+                                        "stats/individual/global.yaml"),
         "r",
     ) as stream:
         try:
@@ -2089,7 +2087,7 @@ def extractnetstats(
 
     with open(
         pkg_resources.resource_filename("pynets",
-                                        "stats/local.yaml"),
+                                        "stats/individual/local.yaml"),
         "r",
     ) as stream:
         try:
@@ -2446,7 +2444,7 @@ def collect_pandas_df_make(
     import os.path as op
     import pandas as pd
     from pynets.core import utils
-    from pynets.stats.utils import build_mp_dict
+    from pynets.statistics.utils import build_mp_dict
     from itertools import groupby
     import re
     from pynets.core.utils import load_runconfig
diff --git a/pynets/stats/global.yaml b/pynets/statistics/individual/global.yaml
similarity index 100%
rename from pynets/stats/global.yaml
rename to pynets/statistics/individual/global.yaml
diff --git a/pynets/stats/local.yaml b/pynets/statistics/individual/local.yaml
similarity index 100%
rename from pynets/stats/local.yaml
rename to pynets/statistics/individual/local.yaml
diff --git a/pynets/stats/multiplex.py b/pynets/statistics/individual/multiplex.py
similarity index 98%
rename from pynets/stats/multiplex.py
rename to pynets/statistics/individual/multiplex.py
index a571609a..227699ad 100644
--- a/pynets/stats/multiplex.py
+++ b/pynets/statistics/individual/multiplex.py
@@ -103,7 +103,6 @@ class of mlib.
       https://doi.org/10.1063/1.4979282
 
     """
-    from pynets.stats.multiplex import countmotifs
 
     mf = countmotifs((in_mat > thr).astype(int), N=N)
     try:
@@ -142,7 +141,6 @@ class of mlib.
       https://doi.org/10.1063/1.4979282
 
     """
-    from pynets.stats.multiplex import adaptivethresh
     from pynets.core.thresholding import threshold_absolute
     from pynets.core.thresholding import standardize
     from scipy import spatial
@@ -438,11 +436,9 @@ def motif_matching(
     import glob
     import pickle
     from pynets.core import thresholding
-    from pynets.stats.multiplex import compare_motifs
     from sklearn.metrics.pairwise import cosine_similarity
-    from pynets.stats.netstats import community_resolution_selection
-    from graspologic.utils import remove_loops, symmetrize, \
-        largest_connected_component
+    from pynets.statistics.individual.algorithms import community_resolution_selection
+    from graspologic.utils import remove_loops, symmetrize
 
     [struct_graph_path, func_graph_path] = paths
     struct_mat = np.load(struct_graph_path)
@@ -669,7 +665,6 @@ def build_multigraphs(est_path_iterlist, ID):
     import numpy as np
     from pathlib import Path
     from pynets.core.utils import flatten
-    from pynets.stats.multiplex import motif_matching
 
     raw_est_path_iterlist = list(
         set(
@@ -683,7 +678,7 @@ def build_multigraphs(est_path_iterlist, ID):
 
     # Available functional and structural connectivity models
     with open(
-        pkg_resources.resource_filename("pynets", "runconfig.yaml"), "r"
+        pkg_resources.resource_filename("pynets", "advanced.yaml"), "r"
     ) as stream:
         hardcoded_params = yaml.load(stream)
         try:
@@ -691,7 +686,7 @@ def build_multigraphs(est_path_iterlist, ID):
         except KeyError:
             print(
                 "ERROR: available functional models not sucessfully extracted"
-                " from runconfig.yaml"
+                " from advanced.yaml"
             )
         try:
             struct_models = hardcoded_params["available_models"][
@@ -699,7 +694,7 @@ def build_multigraphs(est_path_iterlist, ID):
         except KeyError:
             print(
                 "ERROR: available structural models not sucessfully extracted"
-                " from runconfig.yaml"
+                " from advanced.yaml"
             )
     stream.close()
 
diff --git a/pynets/stats/spectral.py b/pynets/statistics/individual/spectral.py
similarity index 95%
rename from pynets/stats/spectral.py
rename to pynets/statistics/individual/spectral.py
index 7c5e5637..a6b93645 100644
--- a/pynets/stats/spectral.py
+++ b/pynets/statistics/individual/spectral.py
@@ -6,8 +6,6 @@
 @author: Derek Pisner
 """
 from pathlib import Path
-import os
-import numpy as np
 import warnings
 
 warnings.filterwarnings("ignore")
@@ -37,10 +35,8 @@ def _omni_embed(pop_array, atlas, graph_path_list,
         n_vertices).
     atlas : str
         The name of an atlas (indicating the node definition).
-    graph_pathlist : list
+    graph_path_list : list
         List of file paths to graphs in pop_array.
-    ID : str
-        An arbitrary subject identifier.
     subgraph_name : str
 
     Returns
@@ -62,11 +58,9 @@ def _omni_embed(pop_array, atlas, graph_path_list,
     import os
     import networkx as nx
     import numpy as np
-    from pynets.core.utils import flatten
     from graspologic.embed.omni import OmnibusEmbed
     from graspologic.embed.mds import ClassicalMDS
-    from joblib import dump
-    from pynets.stats.netstats import CleanGraphs
+    from pynets.statistics.individual.algorithms import CleanGraphs
 
     dir_path = str(Path(os.path.dirname(graph_path_list[0])).parent)
 
@@ -78,7 +72,8 @@ def _omni_embed(pop_array, atlas, graph_path_list,
     i = 0
     for graph_path in graph_path_list:
         if 'thr-' in graph_path:
-            thr = float(graph_path.split('thr-')[1].split('_')[0].split('.npy')[0])
+            thr = float(graph_path.split('thr-'
+                                         )[1].split('_')[0].split('.npy')[0])
         else:
             thr = 1.0
         cg = CleanGraphs(thr,
@@ -141,8 +136,8 @@ def _omni_embed(pop_array, atlas, graph_path_list,
                    f"_subnet-{atlas}_res-{subgraph_name}_" \
                    f"{os.path.basename(graph_path_list[0])}_NULL"
         # TODO: Replace this band-aid solution with the real fix
-        out_path = out_path.replace('subnet-subnet-', 'subnet-').replace('res-res-',
-                                                                'res-')
+        out_path = out_path.replace('subnet-subnet-',
+                                    'subnet-').replace('res-res-', 'res-')
         if not os.path.exists(out_path):
             open(out_path, 'w').close()
     return out_path
@@ -180,7 +175,6 @@ def _mase_embed(pop_array, atlas, graph_path, subgraph_name="all_nodes",
         n_vertices, n_vertices).
     atlas : str
     graph_path : str
-    ID : str
     subgraph_name : str
     n_components : int
 
@@ -256,7 +250,6 @@ def _ase_embed(mat, atlas, graph_path, subgraph_name="all_nodes",
     atlas : str
         The name of an atlas (indicating the node definition).
     graph_path : str
-    ID : str
     subgraph_name : str
 
     Returns
@@ -289,10 +282,8 @@ def _ase_embed(mat, atlas, graph_path, subgraph_name="all_nodes",
     import pickle
     import networkx as nx
     import numpy as np
-    from pynets.core.utils import flatten
     from graspologic.embed.ase import AdjacencySpectralEmbed
-    from joblib import dump
-    from pynets.stats.netstats import CleanGraphs
+    from pynets.statistics.individual.algorithms import CleanGraphs
 
     # Adjacency Spectral embedding
     print(
@@ -337,13 +328,15 @@ def _ase_embed(mat, atlas, graph_path, subgraph_name="all_nodes",
         os.makedirs(namer_dir, exist_ok=True)
 
     out_path = f"{namer_dir}/gradient-ASE" \
-               f"_subnet-{atlas}_res-{subgraph_name}_{os.path.basename(graph_path)}"
+               f"_subnet-{atlas}_res-{subgraph_name}_" \
+               f"{os.path.basename(graph_path)}"
     # out_path_est = f"{namer_dir}/gradient-ASE_{atlas}" \
     #                f"_{subgraph_name}" \
     #                f"_{os.path.basename(graph_path).split('.npy')[0]}.joblib"
 
     # TODO: Replace this band-aid solution with the real fix
-    out_path = out_path.replace('subnet-subnet-', 'subnet-').replace('res-res-', 'res-')
+    out_path = out_path.replace('subnet-subnet-', 'subnet-').replace(
+        'res-res-', 'res-')
     #dump(ase, out_path_est)
 
     print("Saving...")
@@ -369,7 +362,6 @@ def build_asetomes(est_path_iterlist, ID):
     import os
     import numpy as np
     from pynets.core.utils import prune_suffices, flatten
-    from pynets.stats.spectral import _ase_embed
     from pynets.core.utils import load_runconfig
 
     # Available functional and structural connectivity models
@@ -381,7 +373,7 @@ def build_asetomes(est_path_iterlist, ID):
         import sys
         print(
             "ERROR: available gradient dimensionality presets not "
-            "sucessfully extracted from runconfig.yaml"
+            "sucessfully extracted from advanced.yaml"
         )
         sys.exit(1)
 
@@ -419,8 +411,8 @@ def build_asetomes(est_path_iterlist, ID):
                        f"_subnet-{atlas}_res-{subgraph}_" \
                        f"{os.path.basename(file_)}_NULL"
             # TODO: Replace this band-aid solution with the real fix
-            out_path = out_path.replace('subnet-subnet-', 'subnet-').replace('res-res-',
-                                                                    'res-')
+            out_path = out_path.replace('subnet-subnet-',
+                                        'subnet-').replace('res-res-', 'res-')
             if not os.path.exists(out_path):
                 open(out_path, 'w').close()
             out_paths.append(out_path)
@@ -454,7 +446,6 @@ def build_masetome(est_path_iterlist, ID):
     import os
     import numpy as np
     from pynets.core.utils import prune_suffices
-    from pynets.stats.spectral import _mase_embed
     from pynets.core.utils import load_runconfig
 
     # Available functional and structural connectivity models
@@ -466,7 +457,7 @@ def build_masetome(est_path_iterlist, ID):
         import sys
         print(
             "ERROR: available gradient dimensionality presets not "
-            "sucessfully extracted from runconfig.yaml"
+            "sucessfully extracted from advanced.yaml"
         )
         sys.exit(1)
 
@@ -539,7 +530,6 @@ def build_omnetome(est_path_iterlist, ID):
     import sys
     import numpy as np
     from pynets.core.utils import flatten
-    from pynets.stats.spectral import _omni_embed
     from pynets.core.utils import load_runconfig
 
     # Available functional and structural connectivity models
@@ -550,7 +540,7 @@ def build_omnetome(est_path_iterlist, ID):
     except KeyError:
         print(
             "ERROR: available functional models not sucessfully extracted"
-            " from runconfig.yaml"
+            " from advanced.yaml"
         )
         sys.exit(1)
     try:
@@ -559,7 +549,7 @@ def build_omnetome(est_path_iterlist, ID):
     except KeyError:
         print(
             "ERROR: available structural models not sucessfully extracted"
-            " from runconfig.yaml"
+            " from advanced.yaml"
         )
         sys.exit(1)
     try:
@@ -568,7 +558,7 @@ def build_omnetome(est_path_iterlist, ID):
     except KeyError:
         print(
             "ERROR: available gradient dimensionality presets not "
-            "sucessfully extracted from runconfig.yaml"
+            "sucessfully extracted from advanced.yaml"
         )
         sys.exit(1)
 
diff --git a/pynets/statistics/interfaces.py b/pynets/statistics/interfaces.py
new file mode 100755
index 00000000..44932d48
--- /dev/null
+++ b/pynets/statistics/interfaces.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Nov 10 15:44:46 2017
+Copyright (C) 2017
+"""
+import warnings
+import sys
+if sys.platform.startswith('win') is False:
+    import indexed_gzip
+from nipype.interfaces.base import (
+    BaseInterface,
+    BaseInterfaceInputSpec,
+    TraitedSpec,
+    File,
+    traits,
+)
+
+warnings.filterwarnings("ignore")
+
+
+class NetworkAnalysisInputSpec(BaseInterfaceInputSpec):
+    """Input interface wrapper for NetworkAnalysis"""
+
+    ID = traits.Any(mandatory=True)
+    subnet = traits.Any(mandatory=False)
+    thr = traits.Any(mandatory=True)
+    conn_model = traits.Str(mandatory=True)
+    est_path = File(exists=True, mandatory=True)
+    roi = traits.Any(mandatory=False)
+    prune = traits.Any(mandatory=False)
+    norm = traits.Any(mandatory=False)
+    binary = traits.Bool(False, usedefault=True)
+
+
+class NetworkAnalysisOutputSpec(TraitedSpec):
+    """Output interface wrapper for NetworkAnalysis"""
+
+    out_path_neat = File(exists=True, mandatory=True)
+
+
+class NetworkAnalysis(BaseInterface):
+    """Interface wrapper for NetworkAnalysis"""
+
+    input_spec = NetworkAnalysisInputSpec
+    output_spec = NetworkAnalysisOutputSpec
+
+    def _run_interface(self, runtime):
+        from pynets.statistics.individual.algorithms import extractnetstats
+
+        out = extractnetstats(
+            self.inputs.ID,
+            self.inputs.subnet,
+            self.inputs.thr,
+            self.inputs.conn_model,
+            self.inputs.est_path,
+            self.inputs.roi,
+            self.inputs.prune,
+            self.inputs.norm,
+            self.inputs.binary,
+        )
+        setattr(self, "_outpath", out)
+        return runtime
+
+    def _list_outputs(self):
+        import os.path as op
+
+        return {"out_path_neat": op.abspath(getattr(self, "_outpath"))}
diff --git a/pynets/stats/utils.py b/pynets/statistics/utils.py
similarity index 99%
rename from pynets/stats/utils.py
rename to pynets/statistics/utils.py
index 78a37f33..71026692 100644
--- a/pynets/stats/utils.py
+++ b/pynets/statistics/utils.py
@@ -2,11 +2,9 @@
 # -*- coding: utf-8 -*-
 """
 Copyright (C) 2017
-@authors: Derek Pisner
 """
 import pandas as pd
 import os
-import re
 import glob
 import numpy as np
 import itertools
@@ -707,7 +705,6 @@ def populate_subject_dict(
     df_top=None,
 ):
     from colorama import Fore, Style
-    from joblib import Parallel, delayed
     import gc
 
     # print(id)
diff --git a/requirements.txt b/requirements.txt
index a457a9dc..533e49df 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -28,7 +28,6 @@ graspologic>=0.2.0
 sqlalchemy~=1.3.1
 #duecredit>=0.8.0
 h5py>=2.10.0
-templateflow>=0.6.2
 scikit-image>=0.14.2
 six>=1.12.0
 pyyaml>=6.0
diff --git a/setup.py b/setup.py
index be30a642..97ac8495 100644
--- a/setup.py
+++ b/setup.py
@@ -3,10 +3,9 @@
 
 """The setup script."""
 
-from setuptools import setup, find_packages
-#from pynets.__about__ import __version__, DOWNLOAD_URL
-from pynets.__about__ import __version__
+from setuptools import setup
 import versioneer
+from pynets.__about__ import __version__
 cmdclass = versioneer.get_cmdclass()
 
 with open('README.rst') as readme_file:
@@ -41,13 +40,13 @@
     install_requires=requirements,
     dependency_links=['git+https://github.com/dPys/nilearn.git@enh/parc_conn#egg=nilearn',
                       'git+https://github.com/dPys/deepbrain.git@master#egg=deepbrain'],
-    license="GNU General Public License v3",
+    # license="Open Research License",
     zip_safe=False,
     keywords='pynets',
     classifiers=[
-        'Development Status :: 3 - Alpha',
+        'Development Status :: 2 - Beta',
         'Intended Audience :: Developers',
-        'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
+        # 'License :: Open Research License',
         'Natural Language :: English',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',


Test patch:
diff --git a/pynets/config/bids_config_test.json b/pynets/Initializers/bids_config_test.json
similarity index 100%
rename from pynets/config/bids_config_test.json
rename to pynets/Initializers/bids_config_test.json
diff --git a/tests/test_clustering.py b/tests/test_clustering.py
index a765e213..159127ef 100644
--- a/tests/test_clustering.py
+++ b/tests/test_clustering.py
@@ -1,9 +1,6 @@
 #!/usr/bin/env python
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
-
 """
 import pytest
 import numpy as np
@@ -16,7 +13,7 @@
     import cPickle as pickle
 except ImportError:
     import _pickle as pickle
-from pynets.fmri import clustools
+from pynets.fmri import clustering
 import os
 import logging
 
@@ -31,7 +28,7 @@ def test_indx_1dto3d():
     sz = np.random.rand(10, 10, 10)
     idx = 45
 
-    x, y, z = clustools.indx_1dto3d(idx, sz)
+    x, y, z = clustering.indx_1dto3d(idx, sz)
     assert x is not None
     assert y is not None
     assert z is not None
@@ -44,7 +41,7 @@ def test_indx_3dto1d():
     sz = np.random.rand(10, 10, 10)
     idx = 45
 
-    x, y, z = clustools.indx_1dto3d(idx, sz)
+    x, y, z = clustering.indx_1dto3d(idx, sz)
     assert x is not None
     assert y is not None
     assert z is not None
@@ -63,12 +60,12 @@ def test_make_local_connectivity_tcorr():
         f"smoothAROMAnonaggr_bold_short.nii.gz"
     func_img = nib.load(func_file)
     mask_img = nib.load(mask_file)
-    W = clustools.make_local_connectivity_tcorr(func_img, mask_img,
-                                                thresh=0.50)
+    W = clustering.make_local_connectivity_tcorr(func_img, mask_img,
+                                                 thresh=0.50)
 
     assert W is not None
 
-    out_img = clustools.parcellate_ncut(W, 200, mask_img)
+    out_img = clustering.parcellate_ncut(W, 200, mask_img)
 
     assert isinstance(out_img, nib.Nifti1Image)
 
@@ -86,12 +83,12 @@ def test_make_local_connectivity_scorr():
         f"smoothAROMAnonaggr_bold_short.nii.gz"
     func_img = nib.load(func_file)
     mask_img = nib.load(mask_file)
-    W = clustools.make_local_connectivity_scorr(func_img, mask_img,
-                                                thresh=0.50)
+    W = clustering.make_local_connectivity_scorr(func_img, mask_img,
+                                                 thresh=0.50)
 
     assert W is not None
 
-    out_img = clustools.parcellate_ncut(W, 200, mask_img)
+    out_img = clustering.parcellate_ncut(W, 200, mask_img)
 
     assert out_img is not None
 
@@ -122,21 +119,21 @@ def test_ni_parcellate(clust_type):
                 f"task-rest_space-MNI152NLin6Asym_desc-" \
         f"smoothAROMAnonaggr_bold_short.nii.gz"
     func_img = nib.load(func_file)
-    nip = clustools.NiParcellate(func_file=func_file, clust_mask=clust_mask,
-                                 k=k, clust_type=clust_type,
-                                 local_corr=local_corr, outdir=out_dir,
-                                 conf=None, mask=mask)
+    nip = clustering.NiParcellate(func_file=func_file, clust_mask=clust_mask,
+                                  k=k, clust_type=clust_type,
+                                  local_corr=local_corr, outdir=out_dir,
+                                  conf=None, mask=mask)
     atlas = nip.create_clean_mask()
     nip.create_local_clustering(overwrite=True, r_thresh=0.4)
     out_path = f"{str(tmpdir.name)}/parc_tmp.nii.gz"
-    parcellation = clustools.parcellate(func_img, local_corr,
-                              clust_type, nip._local_conn_mat_path,
-                              nip.num_conn_comps,
-                              nip._clust_mask_corr_img,
-                              nip._standardize,
-                              nip._detrending, nip.k, nip._local_conn,
-                              nip.conf, tmpdir.name,
-                              nip._conn_comps)
+    parcellation = clustering.parcellate(func_img, local_corr,
+                                         clust_type, nip._local_conn_mat_path,
+                                         nip.num_conn_comps,
+                                         nip._clust_mask_corr_img,
+                                         nip._standardize,
+                                         nip._detrending, nip.k, nip._local_conn,
+                                         nip.conf, tmpdir.name,
+                                         nip._conn_comps)
 
     nib.save(parcellation, out_path)
     assert out_path is not None
@@ -173,10 +170,10 @@ def test_ni_parcellate_mult_conn_comps(clust_type):
     conf = f"{base_dir}/BIDS/sub-25659/ses-1/func/" \
            f"sub-25659_ses-1_task-rest_desc-confounds_regressors.tsv"
     func_img = nib.load(func_file)
-    nip = clustools.NiParcellate(func_file=func_file, clust_mask=clust_mask,
-                                 k=k, clust_type=clust_type,
-                                 local_corr=local_corr, outdir=out_dir,
-                                 conf=conf, mask=mask)
+    nip = clustering.NiParcellate(func_file=func_file, clust_mask=clust_mask,
+                                  k=k, clust_type=clust_type,
+                                  local_corr=local_corr, outdir=out_dir,
+                                  conf=conf, mask=mask)
 
     atlas = nip.create_clean_mask()
 
@@ -186,14 +183,14 @@ def test_ni_parcellate_mult_conn_comps(clust_type):
     nip.create_local_clustering(overwrite=True, r_thresh=0.4)
     out_path = f"{str(tmpdir.name)}/parc_tmp.nii.gz"
 
-    parcellation = clustools.parcellate(func_img, local_corr,
-                              clust_type, nip._local_conn_mat_path,
-                              nip.num_conn_comps,
-                              nip._clust_mask_corr_img,
-                              nip._standardize,
-                              nip._detrending, nip.k, nip._local_conn,
-                              nip.conf, tmpdir.name,
-                              nip._conn_comps)
+    parcellation = clustering.parcellate(func_img, local_corr,
+                                         clust_type, nip._local_conn_mat_path,
+                                         nip.num_conn_comps,
+                                         nip._clust_mask_corr_img,
+                                         nip._standardize,
+                                         nip._detrending, nip.k, nip._local_conn,
+                                         nip.conf, tmpdir.name,
+                                         nip._conn_comps)
 
     nib.save(parcellation, out_path)
     assert atlas is not None
diff --git a/tests/test_dmri_utils.py b/tests/test_dmri_utils.py
index d292d1b0..85b47b78 100755
--- a/tests/test_dmri_utils.py
+++ b/tests/test_dmri_utils.py
@@ -22,7 +22,8 @@
 def test_extract_b0():
 
     base_dir = str(Path(__file__).parent/"examples")
-    dwi_path = f"{base_dir}/BIDS/sub-25659/ses-1/dwi/final_preprocessed_dwi.nii.gz"
+    dwi_path = f"{base_dir}/BIDS/sub-25659/ses-1/dwi/final_preprocessed_" \
+               f"dwi.nii.gz"
     fbval = f"{base_dir}/BIDS/sub-25659/ses-1/dwi/final_bval.bval"
     b0_ixs = np.where(np.loadtxt(fbval) <= 50)[0].tolist()[:2]
     out_path = dmriutils.extract_b0(dwi_path, b0_ixs)
diff --git a/tests/test_estimation.py b/tests/test_estimation.py
index e4484b8f..badc3bab 100755
--- a/tests/test_estimation.py
+++ b/tests/test_estimation.py
@@ -262,7 +262,8 @@ def test_timseries_extraction_extract(conf):
     if conf:
         conf_file = tempfile.NamedTemporaryFile(mode='w+', suffix='.tsv')
         conf_mat = np.random.rand(length)
-        conf_df = pd.DataFrame({'Conf1': conf_mat, "Conf2": [np.nan]*len(conf_mat)})
+        conf_df = pd.DataFrame({'Conf1': conf_mat,
+                                "Conf2": [np.nan]*len(conf_mat)})
         conf_df.to_csv(conf_file.name, sep='\t', index=False)
         conf = conf_file.name
 
diff --git a/tests/test_netstats.py b/tests/test_netstats.py
index 893c4cbf..bf9cc28c 100644
--- a/tests/test_netstats.py
+++ b/tests/test_netstats.py
@@ -9,7 +9,7 @@
 import networkx as nx
 import time
 from pathlib import Path
-from pynets.stats import netstats
+from pynets.statistics.individual import algorithms
 import logging
 
 logger = logging.getLogger(__name__)
@@ -22,7 +22,7 @@ def test_get_prop_type(value):
     Test for get_prop_type() functionality
     """
     start_time = time.time()
-    tname, value, key = netstats.get_prop_type(value)
+    tname, value, key = algorithms.get_prop_type(value)
     if value == 2:
         value = int(value)
     print("%s%s%s" % (
@@ -54,7 +54,7 @@ def test_nx2gt():
     nxG = nx.from_numpy_array(in_mat)
 
     start_time = time.time()
-    gtG = netstats.nx2gt(nxG)
+    gtG = algorithms.nx2gt(nxG)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     np.round(time.time() - start_time, 1), 's'))
@@ -77,7 +77,7 @@ def test_np2gt():
         f"thrtype-PROP_thr-0.95.npy")
 
     start_time = time.time()
-    Gt = netstats.np2gt(in_mat)
+    Gt = algorithms.np2gt(in_mat)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     np.round(time.time() - start_time, 1), 's'))
@@ -101,15 +101,31 @@ def test_average_shortest_path_length_for_all():
     G = nx.from_numpy_array(in_mat)
 
     start_time = time.time()
-    avgest_path_len = netstats.average_shortest_path_length_for_all(G)
+    avgest_path_len = algorithms.average_shortest_path_length_for_all(G)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     np.round(time.time() - start_time, 1), 's'))
     assert avgest_path_len > 0
-    assert type(avgest_path_len) == float
+    assert type(avgest_path_len) == np.float64
 
+@pytest.mark.parametrize("weight", ["weight", "not_weight"])
+def test_average_shortest_path_length_fast(weight):
+    """
+    Test for average_shortest_path_length_fast functionality
+    """
+    base_dir = str(Path(__file__).parent/"examples")
+    in_mat = np.load(f"{base_dir}/miscellaneous/graphs/002_modality-func_rsn-Default_model-cov_nodetype-spheres-2mm_smooth-2fwhm_hpass-0.1Hz_thrtype-PROP_thr-0.95.npy")
+    G = nx.from_numpy_array(in_mat)
 
-def test_average_local_efficiency():
+    start_time = time.time()
+    avgest_path_len = algorithms.average_shortest_path_length_fast(G, weight=weight)
+    print("%s%s%s" % ('test_average_shortest_path_length_fast --> finished: ',
+                      np.round(time.time() - start_time, 1), 's'))
+    assert avgest_path_len > 0
+    assert type(avgest_path_len) == np.float64
+
+@pytest.mark.parametrize("engine", ["GT", "NX"])
+def test_average_local_efficiency(engine):
     """
     Test for average_local_efficiency functionality
     """
@@ -121,13 +137,12 @@ def test_average_local_efficiency():
     G = nx.from_numpy_array(in_mat)
 
     start_time = time.time()
-    average_local_efficiency = netstats.average_local_efficiency(G,
-                                                                 engine='nx')
-    print("%s%s%s" % (
-    'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
-    np.round(time.time() - start_time, 1), 's'))
+    average_local_efficiency = algorithms.average_local_efficiency(
+        G, engine=engine)
+    print("%s%s%s" % ('test_average_local_efficiency --> finished: ',
+                      np.round(time.time() - start_time, 1), 's'))
     assert average_local_efficiency > 0
-    assert average_local_efficiency.dtype == float
+    assert type(average_local_efficiency) == np.float64
 
 
 # used random node_comm_aff_mat
@@ -140,7 +155,7 @@ def test_create_communities():
     node_num = 3
 
     start_time = time.time()
-    com_assign = netstats.create_communities(node_comm_aff_mat, node_num)
+    com_assign = algorithms.create_communities(node_comm_aff_mat, node_num)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     np.round(time.time() - start_time, 1), 's'))
@@ -162,7 +177,7 @@ def test_participation_coef(degree):
     W = np.random.rand(ci_dim, ci_dim)
 
     start_time = time.time()
-    P = netstats.participation_coef(W, ci, degree=degree)
+    P = algorithms.participation_coef(W, ci, degree=degree)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     str(np.round(time.time() - start_time, 1)), 's'))
@@ -187,7 +202,7 @@ def test_modularity():
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     str(np.round(time.time() - start_time, 1)), 's'))
     assert type(ci_dict) == dict
-    assert type(mod) == float
+    assert type(mod) == np.float64
 
 
 def test_diversity_coef_sign():
@@ -204,7 +219,7 @@ def test_diversity_coef_sign():
     W = np.random.rand(ci_dim, ci_dim)
 
     start_time = time.time()
-    [Hpos, Hneg] = netstats.diversity_coef_sign(W, ci)
+    [Hpos, Hneg] = algorithms.diversity_coef_sign(W, ci)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     str(np.round(time.time() - start_time, 1)), 's'))
@@ -230,7 +245,7 @@ def test_link_communities(clustering):
         f"{base_dir}/miscellaneous/sub-0021001_rsn-Default_nodetype-parc_"
         f"model-sps_template-MNI152_T1_thrtype-DENS_thr-0.19.npy")
     start_time = time.time()
-    M = netstats.link_communities(in_mat, type_clustering=clustering)
+    M = algorithms.link_communities(in_mat, type_clustering=clustering)
     print("%s%s%s" % ('Link Communities --> finished: ',
                       str(np.round(time.time() - start_time, 1)), 's'))
     assert type(M) is np.ndarray
@@ -255,7 +270,7 @@ def test_prune_disconnected(connected_case, fallback_lcc):
         G.add_edge(1, 2)
         G.add_node(3)
     start_time = time.time()
-    [G_out, pruned_nodes] = netstats.prune_disconnected(
+    [G_out, pruned_nodes] = algorithms.prune_disconnected(
         G, fallback_lcc=fallback_lcc)
     print("%s%s%s" % ('Pruning disconnected test --> finished: ',
                       str(np.round(time.time() - start_time, 1)), 's'))
@@ -267,10 +282,9 @@ def test_prune_disconnected(connected_case, fallback_lcc):
         assert len(pruned_nodes) > 0
         assert len(list(G_out.nodes())) < len(list(G.nodes()))
 
-
-@pytest.mark.parametrize("method", ["betweenness", "richclub", "coreness",
-                                    "eigenvector"])
-def test_most_important(method):
+@pytest.mark.parametrize("method", ["betweenness", "richclub", "coreness", "eigenvector"])
+@pytest.mark.parametrize("engine", ["GT", "NX"])
+def test_most_important(method, engine):
     """
     Test pruning for most important nodes functionality
     """
@@ -282,10 +296,9 @@ def test_most_important(method):
     G = nx.from_numpy_array(in_mat)
 
     start_time = time.time()
-    Gt, pruned_nodes = netstats.most_important(G, method=method)
-    print("%s%s%s" % (
-    'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
-    str(np.round(time.time() - start_time, 1)), 's'))
+    Gt, pruned_nodes = algorithms.most_important(G, method=method, engine=engine)
+    print("%s%s%s" % ('test_most_important --> finished: ',
+                      str(np.round(time.time() - start_time, 1)), 's'))
 
     assert Gt is not None
     assert pruned_nodes is not None
@@ -312,9 +325,9 @@ def test_extractnetstats(binary, prune, norm, conn_model):
     roi = None
 
     start_time = time.time()
-    out_path = netstats.extractnetstats(ID, subnet, thr, conn_model, est_path,
-                                        roi, prune,
-                                        norm, binary)
+    out_path = algorithms.extractnetstats(ID, subnet, thr, conn_model, est_path,
+                                          roi, prune,
+                                          norm, binary)
     print("%s%s%s" % (
     'thresh_and_fit (Functional, proportional thresholding) --> finished: ',
     str(np.round(time.time() - start_time, 1)), 's'))
@@ -333,39 +346,42 @@ def test_extractnetstats(binary, prune, norm, conn_model):
     est_path = f_temp.name
 
     try:
-        out_path = netstats.extractnetstats(ID, subnet, thr, conn_model,
-                                            est_path, roi, prune,
-                                            norm, binary)
+        out_path = algorithms.extractnetstats(ID, subnet, thr, conn_model,
+                                              est_path, roi, prune,
+                                              norm, binary)
     except PermissionError:
         pass
 
-
-def test_raw_mets():
+@pytest.mark.parametrize("engine", ["GT", "NX"])
+def test_raw_mets(engine):
     """
     Test raw_mets extraction functionality
     """
-    from pynets.stats.netstats import global_efficiency, \
-        average_local_efficiency
+    from pynets.statistics.individual.algorithms import global_efficiency, \
+        average_local_efficiency, smallworldness
     from networkx.algorithms import degree_assortativity_coefficient, \
         average_clustering, average_shortest_path_length, \
         degree_pearson_correlation_coefficient, graph_number_of_cliques, \
-        transitivity, sigma
-    base_dir = str(Path(__file__).parent / "examples")
-    est_path = f"{base_dir}/miscellaneous/sub-0021001_rsn-Default_" \
-               f"nodetype-parc_model-sps_template-MNI152_T1_thrtype-DENS_" \
-               f"thr-0.19.npy"
+        transitivity
+    base_dir = str(Path(__file__).parent/"examples")
+    est_path = f"{base_dir}/miscellaneous/sub-0021001_rsn-Default_nodetype-" \
+               f"parc_model-sps_template-MNI152_T1_thrtype-DENS_thr-0.19.npy"
     in_mat = np.load(est_path)
     G = nx.from_numpy_array(in_mat)
-    [G, _] = netstats.prune_disconnected(G)
+    [G, _] = algorithms.prune_disconnected(G)
     metric_list_glob = [global_efficiency, average_local_efficiency,
                         degree_assortativity_coefficient,
                         average_clustering, average_shortest_path_length,
                         degree_pearson_correlation_coefficient,
-                        graph_number_of_cliques, transitivity]
+                        graph_number_of_cliques, transitivity,
+                        smallworldness]
     for i in metric_list_glob:
-        net_met_val = netstats.raw_mets(G, i, engine='nx')
+        net_met_val = algorithms.raw_mets(G, i, engine=engine)
         assert net_met_val is not np.nan
-        assert type(net_met_val) == float
+        if (engine == 'nx'):
+            assert type(net_met_val) == np.float64
+        elif (engine == 'gt'):
+            assert type(net_met_val) == np.float64
 
 
 def test_subgraph_number_of_cliques_for_all():
@@ -379,7 +395,7 @@ def test_subgraph_number_of_cliques_for_all():
         f"pe-spheres-2mm_smooth-2fwhm_hpass-0.1Hz_thrtype-PROP_thr-0.95.npy")
     G = nx.from_numpy_array(in_mat)
 
-    cliques = netstats.subgraph_number_of_cliques_for_all(G)
+    cliques = algorithms.subgraph_number_of_cliques_for_all(G)
 
     assert cliques > 0
 
@@ -415,9 +431,9 @@ def test_smallworldness(approach, reference):
     in_mat = np.load(est_path)
     G = nx.from_numpy_array(in_mat)
 
-    sigma = netstats.smallworldness(G, niter=5, nrand=5,
-                                    approach=approach,
-                                    reference=reference, engine='nx')
+    sigma = algorithms.smallworldness(G, niter=5, nrand=5,
+                                      approach=approach,
+                                      reference=reference, engine='nx')
 
     # A subnet is smallworld if sigma > 1
     assert sigma < 1
@@ -437,7 +453,7 @@ def test_participation_coef_sign():
     ci_dim = int(np.shape(ci)[0])
     W = np.random.rand(ci_dim, ci_dim)
 
-    Ppos, Pneg = netstats.participation_coef_sign(W, ci)
+    Ppos, Pneg = algorithms.participation_coef_sign(W, ci)
 
     assert len(Ppos) == ci_dim and len(Pneg) == ci_dim
 
@@ -459,7 +475,7 @@ def test_weighted_transitivity(binarize):
 
     G = nx.from_numpy_array(in_mat)
 
-    transitivity = netstats.weighted_transitivity(G)
+    transitivity = algorithms.weighted_transitivity(G)
 
     assert transitivity <= 3 and transitivity >= 0
 
@@ -490,7 +506,7 @@ def test_clean_graphs(fmt, conn_model, prune, norm):
                    f"model-sps_thrtype-PROP_thr-0.94.txt"
         in_mat = np.genfromtxt(est_path)
 
-    clean = netstats.CleanGraphs(0.5, conn_model, est_path, prune, norm)
+    clean = algorithms.CleanGraphs(0.5, conn_model, est_path, prune, norm)
     clean.normalize_graph()
     clean.print_summary()
     clean.create_length_matrix()
@@ -517,8 +533,8 @@ def test_save_netmets():
     metric_list_names = ['metric_a', 'metric_b', 'metric_c']
     net_met_val_list_final = [1, 2, 3]
 
-    netstats.save_netmets(dir_path, est_path, metric_list_names,
-                          net_met_val_list_final)
+    algorithms.save_netmets(dir_path, est_path, metric_list_names,
+                            net_met_val_list_final)
 
 
 @pytest.mark.parametrize("true_metric", [True, False])
@@ -540,7 +556,7 @@ def test_iterate_nx_global_measures(true_metric):
     else:
         metric_list_glob = ['<function fake_func at 0x7f8b7129b700>']
 
-    netstats.iterate_nx_global_measures(G, metric_list_glob)
+    algorithms.iterate_nx_global_measures(G, metric_list_glob)
 
 
 @pytest.mark.parametrize("sim_num_comms", [1, 5, 10])
@@ -551,18 +567,17 @@ def test_community_resolution_selection(sim_num_comms, sim_size):
           netstats.community_resolution_selection.
     """
     G = nx.caveman_graph(sim_num_comms, sim_size)
-    node_ci, ci, resolution, num_comms = netstats.community_resolution_selection(
+    node_ci, ci, resolution, num_comms = algorithms.community_resolution_selection(
         G)
 
     assert len(node_ci) == len(ci)
     assert num_comms == sim_num_comms
     assert resolution is not None
 
-
-@pytest.mark.parametrize("metric",
-                         ['participation', 'diversity', 'local_efficiency',
-                          'comm_centrality', 'rich_club_coeff'])
-def test_get_metrics(metric):
+@pytest.mark.parametrize("metric", ['participation', 'diversity', 'local_efficiency',
+                                    'comm_centrality', 'rich_club_coeff'])
+@pytest.mark.parametrize("engine", ["GT", "NX"])
+def test_get_metrics(metric, engine):
     """
     Test various wrappers for getting nx graph metrics
     """
@@ -579,39 +594,37 @@ def test_get_metrics(metric):
 
     if metric == 'participation':
         metric_list_names, net_met_val_list_final = \
-            netstats.get_participation(in_mat, ci, metric_list_names,
-                                       net_met_val_list_final)
+            algorithms.get_participation(in_mat, ci, metric_list_names,
+                                         net_met_val_list_final)
         assert len(metric_list_names) == len(
-            netstats.participation_coef(in_mat, ci)) + 1
+            algorithms.participation_coef(in_mat, ci)) + 1
         assert len(net_met_val_list_final) == len(
-            netstats.participation_coef(in_mat, ci)) + 1
+            algorithms.participation_coef(in_mat, ci)) + 1
     elif metric == 'diversity':
         metric_list_names, net_met_val_list_final = \
-            netstats.get_diversity(in_mat, ci, metric_list_names,
-                                   net_met_val_list_final)
+            algorithms.get_diversity(in_mat, ci, metric_list_names,
+                                     net_met_val_list_final)
         assert len(metric_list_names) == \
-               np.shape(netstats.diversity_coef_sign(in_mat, ci))[1] + 1
+               np.shape(algorithms.diversity_coef_sign(in_mat, ci))[1] + 1
         assert len(net_met_val_list_final) == \
-               np.shape(netstats.diversity_coef_sign(in_mat, ci))[1] + 1
+               np.shape(algorithms.diversity_coef_sign(in_mat, ci))[1] + 1
     elif metric == 'local_efficiency':
         metric_list_names, net_met_val_list_final = \
-            netstats.get_local_efficiency(G, metric_list_names,
-                                          net_met_val_list_final)
-        assert len(metric_list_names) == len(netstats.local_efficiency(G)) + 1
-        assert len(net_met_val_list_final) == len(
-            netstats.local_efficiency(G)) + 1
+            algorithms.get_local_efficiency(G, metric_list_names, net_met_val_list_final)
+        assert len(metric_list_names) == len(algorithms.local_efficiency(G, engine=engine)) + 1
+        assert len(net_met_val_list_final) == len(algorithms.local_efficiency(G, engine=engine)) + 1
     elif metric == 'comm_centrality':
         metric_list_names, net_met_val_list_final = \
-            netstats.get_comm_centrality(G, metric_list_names,
-                                         net_met_val_list_final)
+            algorithms.get_comm_centrality(G, metric_list_names,
+                                           net_met_val_list_final)
         assert len(metric_list_names) == len(
             nx.algorithms.communicability_betweenness_centrality(G)) + 1
         assert len(net_met_val_list_final) == len(
             nx.algorithms.communicability_betweenness_centrality(G)) + 1
     elif metric == 'rich_club_coeff':
         metric_list_names, net_met_val_list_final = \
-            netstats.get_rich_club_coeff(G, metric_list_names,
-                                         net_met_val_list_final)
+            algorithms.get_rich_club_coeff(G, metric_list_names,
+                                           net_met_val_list_final)
         assert len(metric_list_names) == len(
             nx.algorithms.rich_club_coefficient(G)) + 1
         assert len(net_met_val_list_final) == len(
@@ -652,7 +665,7 @@ def test_collect_pandas_df_make(plot_switch, sql_out, embed, create_summary,
             f"{base_dir}/topology/metrics_sub-0021001_modality-dwi_"
             f"nodetype-parc_model-csa_thrtype-PROP_thr-0.3.csv"]
 
-    combination_complete = netstats.collect_pandas_df_make(
+    combination_complete = algorithms.collect_pandas_df_make(
         net_mets_csv_list, ID, subnet, plot_switch=plot_switch, embed=embed,
         create_summary=create_summary, sql_out=sql_out)
 
diff --git a/tests/test_nodemaker.py b/tests/test_nodemaker.py
index dd527fc6..82c10d79 100755
--- a/tests/test_nodemaker.py
+++ b/tests/test_nodemaker.py
@@ -2,15 +2,13 @@
 # -*- coding: utf-8 -*-
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
-
 """
 import pytest
 import numpy as np
 import time
 import nibabel as nib
 import os
+
 try:
     import cPickle as pickle
 except ImportError:
@@ -28,11 +26,17 @@
 logger.setLevel(50)
 
 
-@pytest.mark.parametrize("atlas", ['atlas_aal', 'atlas_talairach_gyrus',
-                                   'atlas_talairach_ba',
-                                   'atlas_talairach_lobe',
-                                   'atlas_harvard_oxford',
-                                   'atlas_destrieux_2009'])
+@pytest.mark.parametrize(
+    "atlas",
+    [
+        "atlas_aal",
+        "atlas_talairach_gyrus",
+        "atlas_talairach_ba",
+        "atlas_talairach_lobe",
+        "atlas_harvard_oxford",
+        "atlas_destrieux_2009",
+    ],
+)
 def test_nilearn_atlas_helper(atlas):
     parc = False
     [labels, networks_list, parlistfile] = \
@@ -41,15 +45,21 @@ def test_nilearn_atlas_helper(atlas):
     print(networks_list)
     print(parlistfile)
     assert labels is not None
-    if (parlistfile is not None) and isinstance(labels[0], str) and \
-        isinstance(parlistfile, str) and (atlas != 'atlas_aal') and \
-        os.path.isfile(parlistfile):
+    if (
+        (parlistfile is not None)
+        and isinstance(labels[0], str)
+        and isinstance(parlistfile, str)
+        and (atlas != "atlas_aal")
+        and os.path.isfile(parlistfile)
+    ):
         parcel_data = nib.load(parlistfile).get_fdata()
-        assert len(labels) == len(np.unique(parcel_data)) or \
-               len(labels) == len(np.unique(parcel_data)) - 1 or \
-               len(labels)-1 == len(np.unique(parcel_data)) or \
-               float(2*len(labels)) == float(len(np.unique(parcel_data)) - 1) \
-               or float(2*(len(labels))-1) == len(np.unique(parcel_data))
+        assert (
+            len(labels) == len(np.unique(parcel_data))
+            or len(labels) == len(np.unique(parcel_data)) - 1
+            or len(labels) - 1 == len(np.unique(parcel_data))
+            or float(2 * len(labels)) == float(len(np.unique(parcel_data)) - 1)
+            or float(2 * (len(labels)) - 1) == len(np.unique(parcel_data))
+        )
 
 
 def test_nodemaker_tools_parlistfile_RSN():
@@ -57,23 +67,29 @@ def test_nodemaker_tools_parlistfile_RSN():
     Test nodemaker_tools_parlistfile_RSN functionality
     """
     # Set example inputs
-    template = pkg_resources.resource_filename("pynets",
-                                               f"templates/MNI152_T1_brain_"
-                                               f"2mm.nii.gz")
+    template = pkg_resources.resource_filename(
+        "pynets", f"templates/MNI152_T1_brain_" f"2mm.nii.gz"
+    )
     dir_path = str(tempfile.TemporaryDirectory().name)
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_"
-                                           "cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
     os.makedirs(dir_path, exist_ok=True)
     shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}")
     parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}"
-    network = 'Default'
+    network = "Default"
     parc = True
 
     start_time = time.time()
     [coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile)
-    print("%s%s%s" % ('get_names_and_coords_of_parcels --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "get_names_and_coords_of_parcels --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0
                                         ].tolist()
@@ -82,18 +98,43 @@ def test_nodemaker_tools_parlistfile_RSN():
 
     parcel_list = nodemaker.gen_img_list(parlistfile)
 
-    [net_coords, net_parcel_list, net_labels, network] = nodemaker.get_node_membership(network, template, coords,
-                                                                                       labels, parc, parcel_list)
-    print("%s%s%s" % ('get_node_membership --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    [net_coords, net_parcel_list, net_labels, network] = \
+        nodemaker.get_node_membership(network, template, coords, labels,
+                                      parc, parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "get_node_membership --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
-    [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(net_parcel_list)
-    print("%s%s%s" % ('create_parcel_atlas --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
+    [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(
+        net_parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "create_parcel_atlas --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
-    out_path = nodemaker.gen_network_parcels(parlistfile, network, net_labels, dir_path)
-    print("%s%s%s" % ('gen_network_parcels --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
+    out_path = nodemaker.gen_network_parcels(parlistfile, network, net_labels,
+                                             dir_path)
+    print(
+        "%s%s%s"
+        % (
+            "gen_network_parcels --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert coords is not None
     assert net_coords is not None
@@ -105,24 +146,42 @@ def test_nodemaker_tools_parlistfile_RSN():
     assert network is not None
 
 
-@pytest.mark.parametrize("atlas", ['coords_dosenbach_2010', 'coords_power_2011'])
+@pytest.mark.parametrize("atlas", ["coords_dosenbach_2010",
+                                   "coords_power_2011"])
 def test_nodemaker_tools_nilearn_coords_RSN(atlas):
     """
     Test nodemaker_tools_nilearn_coords_RSN functionality
     """
     # Set example inputs
-    template = pkg_resources.resource_filename("pynets", f"templates/MNI152_T1_brain_2mm.nii.gz")
-    network = 'Default'
+    template = pkg_resources.resource_filename(
+        "pynets", f"templates/MNI152_T1_brain_2mm.nii.gz"
+    )
+    network = "Default"
     parc = False
     parcel_list = None
     start_time = time.time()
     [coords, _, _, labels] = nodemaker.fetch_nilearn_atlas_coords(atlas)
-    print("%s%s%s" % ('fetch_nilearn_atlas_coords --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "fetch_nilearn_atlas_coords --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
     [net_coords, _, net_labels, network] = nodemaker.get_node_membership(
-        network, template, coords, labels, parc, parcel_list)
-    print("%s%s%s" % ('get_node_membership --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
+        network, template, coords, labels, parc, parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "get_node_membership --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert coords is not None
     assert labels is not None
@@ -137,54 +196,104 @@ def test_nodemaker_tools_masking_parlistfile_RSN():
     """
     # Set example inputs
 
-    template = pkg_resources.resource_filename("pynets",
-                                               f"templates/MNI152_T1_brain_2mm.nii.gz")
+    template = pkg_resources.resource_filename(
+        "pynets", f"templates/MNI152_T1_brain_2mm.nii.gz"
+    )
 
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
     shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}")
     parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}"
 
-    roi = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
+    roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
     data_gen.generate_mni_space_img()[1].to_filename(roi)
 
-    network = 'Default'
-    ID = '002'
+    network = "Default"
+    ID = "002"
     perc_overlap = 0.10
     parc = True
 
     start_time = time.time()
     [coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(parlistfile)
-    print("%s%s%s" % ('get_names_and_coords_of_parcels --> finished: ',
-    str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "get_names_and_coords_of_parcels --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
-    labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0].tolist()
+    labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0
+                                        ].tolist()
 
     start_time = time.time()
     parcel_list = nodemaker.gen_img_list(parlistfile)
-    [net_coords, net_parcel_list, net_labels, network] = nodemaker.get_node_membership(network, template, coords,
-                                                                                       labels, parc, parcel_list)
-    print("%s%s%s" % ('get_node_membership --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
+    [net_coords, net_parcel_list, net_labels, network] = \
+        nodemaker.get_node_membership(
+        network, template, coords, labels, parc, parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "get_node_membership --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
-    [net_coords_masked, net_labels_masked, net_parcel_list_masked] = nodemaker.parcel_masker(roi, net_coords,
-                                                                                                  net_parcel_list,
-                                                                                                  net_labels,
-                                                                                                  dir_path, ID,
-                                                                                                  perc_overlap, vox_size='2mm')
-    print("%s%s%s" % ('parcel_masker --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
+    [
+        net_coords_masked,
+        net_labels_masked,
+        net_parcel_list_masked,
+    ] = nodemaker.parcel_masker(
+        roi,
+        net_coords,
+        net_parcel_list,
+        net_labels,
+        dir_path,
+        ID,
+        perc_overlap,
+        vox_size="2mm",
+    )
+    print(
+        "%s%s%s"
+        % (
+            "parcel_masker --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
-    [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(net_parcel_list_masked)
-    print("%s%s%s" % ('create_parcel_atlas --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    [net_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(
+        net_parcel_list_masked
+    )
+    print(
+        "%s%s%s"
+        % (
+            "create_parcel_atlas --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
-    out_path = nodemaker.gen_network_parcels(parlistfile, network, net_labels_masked, dir_path)
-    print("%s%s%s" % ('gen_network_parcels --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    out_path = nodemaker.gen_network_parcels(
+        parlistfile, network, net_labels_masked, dir_path
+    )
+    print(
+        "%s%s%s"
+        % (
+            "gen_network_parcels --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert coords is not None
     assert net_coords is not None
@@ -199,43 +308,62 @@ def test_nodemaker_tools_masking_parlistfile_RSN():
     assert network is not None
 
 
-@pytest.mark.parametrize("atlas", ['coords_dosenbach_2010',
-                                   'coords_power_2011'])
+@pytest.mark.parametrize("atlas", ["coords_dosenbach_2010",
+                                   "coords_power_2011"])
 def test_nodemaker_tools_masking_coords_RSN(atlas):
     """
     Test nodemaker_tools_masking_coords_RSN functionality
     """
     # Set example inputs
-    template = pkg_resources.resource_filename("pynets",
-                                               f"templates/MNI152_T1_brain_"
-                                               f"2mm.nii.gz")
+    template = pkg_resources.resource_filename(
+        "pynets", f"templates/MNI152_T1_brain_" f"2mm.nii.gz"
+    )
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
-    roi = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
+    roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
     data_gen.generate_mni_space_img()[1].to_filename(roi)
 
-    network = 'Default'
+    network = "Default"
     parc = False
     parcel_list = None
     error = 2
     start_time = time.time()
     [coords, _, _, labels] = nodemaker.fetch_nilearn_atlas_coords(atlas)
-    print("%s%s%s" % ('fetch_nilearn_atlas_coords (Masking subnet version) --> '
-                      'finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "fetch_nilearn_atlas_coords (Masking subnet version) --> "
+            "finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
     [net_coords, _, net_labels, network] = nodemaker.get_node_membership(
-        network, template, coords, labels, parc, parcel_list)
-    print("%s%s%s" % ('get_node_membership (Masking subnet version) --> '
-                      'finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+        network, template, coords, labels, parc, parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "get_node_membership (Masking subnet version) --> " "finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
     [net_coords_masked, net_labels_masked] = nodemaker.coords_masker(
-        roi, net_coords, net_labels, error)
-    print("%s%s%s" % ('coords_masker (Masking subnet version) --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+        roi, net_coords, net_labels, error
+    )
+    print(
+        "%s%s%s"
+        % (
+            "coords_masker (Masking subnet version) --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert coords is not None
     assert net_coords is not None
@@ -250,9 +378,9 @@ def test_nodemaker_tools_parlistfile_WB():
     Test nodemaker_tools_parlistfile_WB functionality
     """
     # Set example inputs
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_"
-                                           "cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
 
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
@@ -262,21 +390,35 @@ def test_nodemaker_tools_parlistfile_WB():
     start_time = time.time()
     [WB_coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(
         parlistfile)
-    print("%s%s%s" % ('get_names_and_coords_of_parcels (User-atlas '
-                      'whole-brain version) --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "get_names_and_coords_of_parcels (User-atlas "
+            "whole-brain version) --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
-    WB_labels = np.arange(len(WB_coords) + 1)[np.arange(len(WB_coords) + 1)
-                                              != 0].tolist()
+    WB_labels = np.arange(len(WB_coords) + 1)[
+        np.arange(len(WB_coords) + 1) != 0
+    ].tolist()
 
     start_time = time.time()
 
     WB_parcel_list = nodemaker.gen_img_list(parlistfile)
     [WB_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(
-        WB_parcel_list)
-    print("%s%s%s" % ('create_parcel_atlas (User-atlas whole-brain version) '
-                      '--> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+        WB_parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "create_parcel_atlas (User-atlas whole-brain version) "
+            "--> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert WB_coords is not None
     assert WB_labels is not None
@@ -285,17 +427,22 @@ def test_nodemaker_tools_parlistfile_WB():
     assert parcel_list_exp is not None
 
 
-@pytest.mark.parametrize("atlas", ['coords_dosenbach_2010',
-                                   'coords_power_2011'])
+@pytest.mark.parametrize("atlas", ["coords_dosenbach_2010",
+                                   "coords_power_2011"])
 def test_nodemaker_tools_nilearn_coords_WB(atlas):
     """
     Test nodemaker_tools_nilearn_coords_WB functionality
     """
     start_time = time.time()
     [WB_coords, _, _, WB_labels] = nodemaker.fetch_nilearn_atlas_coords(atlas)
-    print("%s%s%s" % ('fetch_nilearn_atlas_coords (Whole-brain version) --> '
-                      'finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "fetch_nilearn_atlas_coords (Whole-brain version) --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert WB_coords is not None
     assert WB_labels is not None
@@ -306,69 +453,127 @@ def test_nodemaker_tools_masking_parlistfile_WB():
     Test nodemaker_tools_masking_parlistfile_WB functionality
     """
     # Set example inputs
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_"
-                                           "cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
     shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}")
     parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}"
 
-    atlas = 'whole_brain_cluster_labels_PCA200'
-    roi = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
+    atlas = "whole_brain_cluster_labels_PCA200"
+    roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
     data_gen.generate_mni_space_img()[1].to_filename(roi)
-    ID = '002'
+    ID = "002"
     parc = True
     perc_overlap = 0.10
 
     start_time = time.time()
     [WB_coords, _, _, _] = nodemaker.get_names_and_coords_of_parcels(
         parlistfile)
-    print("%s%s%s" % ('get_names_and_coords_of_parcels (Masking whole-brain '
-                      'version) --> finished: ',
-    str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "get_names_and_coords_of_parcels (Masking whole-brain "
+            "version) --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
-    WB_labels = np.arange(len(WB_coords) + 1)[np.arange(len(WB_coords) + 1)
-                                              != 0].tolist()
+    WB_labels = np.arange(len(WB_coords) + 1)[
+        np.arange(len(WB_coords) + 1) != 0
+    ].tolist()
 
     start_time = time.time()
 
     WB_parcel_list = nodemaker.gen_img_list(parlistfile)
 
     start_time = time.time()
-    [WB_net_parcels_map_nifti_unmasked, WB_coords_unmasked, _,
-     _, _, dir_path] = nodemaker.node_gen(WB_coords, WB_parcel_list,
-                                          WB_labels, dir_path, ID, parc, atlas,
-                                          parlistfile)
-    print("%s%s%s" % ('node_gen (Masking whole-brain version) --> finished: ',
-    np.round(time.time() - start_time, 1), 's'))
+    [
+        WB_net_parcels_map_nifti_unmasked,
+        WB_coords_unmasked,
+        _,
+        _,
+        _,
+        dir_path,
+    ] = nodemaker.node_gen(
+        WB_coords, WB_parcel_list, WB_labels, dir_path, ID, parc, atlas,
+        parlistfile
+    )
+    print(
+        "%s%s%s"
+        % (
+            "node_gen (Masking whole-brain version) --> finished: ",
+            np.round(time.time() - start_time, 1),
+            "s",
+        )
+    )
 
     start_time = time.time()
     WB_parcel_list = nodemaker.gen_img_list(parlistfile)
     [WB_parcels_map_nifti, parcel_list_exp] = nodemaker.create_parcel_atlas(
-        WB_parcel_list)
-    print("%s%s%s" % ('create_parcel_atlas (Masking whole-brain version) --> '
-                      'finished: ',
-    np.round(time.time() - start_time, 1), 's'))
+        WB_parcel_list
+    )
+    print(
+        "%s%s%s"
+        % (
+            "create_parcel_atlas (Masking whole-brain version) --> finished: ",
+            np.round(time.time() - start_time, 1),
+            "s",
+        )
+    )
 
     start_time = time.time()
     WB_parcel_list = nodemaker.gen_img_list(parlistfile)
-    [WB_net_parcels_map_nifti_masked, WB_coords_masked, WB_labels_masked,
-     _, _, _] = nodemaker.node_gen_masking(roi, WB_coords, WB_parcel_list,
-                                           WB_labels, dir_path, ID, parc,
-                                           atlas, parlistfile, vox_size='2mm')
+    [
+        WB_net_parcels_map_nifti_masked,
+        WB_coords_masked,
+        WB_labels_masked,
+        _,
+        _,
+        _,
+    ] = nodemaker.node_gen_masking(
+        roi,
+        WB_coords,
+        WB_parcel_list,
+        WB_labels,
+        dir_path,
+        ID,
+        parc,
+        atlas,
+        parlistfile,
+        vox_size="2mm",
+    )
 
     WB_parcel_list = nodemaker.gen_img_list(parlistfile)
     [_, _, WB_parcel_list_masked] = nodemaker.parcel_masker(
-        roi, WB_coords, WB_parcel_list, WB_labels, dir_path, ID,
-        perc_overlap, vox_size='2mm')
-    print("%s%s%s" % ('parcel_masker (Masking whole-brain version) --> '
-                      'finished: ',
-    np.round(time.time() - start_time, 1), 's'))
+        roi,
+        WB_coords,
+        WB_parcel_list,
+        WB_labels,
+        dir_path,
+        ID,
+        perc_overlap,
+        vox_size="2mm",
+    )
+    print(
+        "%s%s%s"
+        % (
+            "parcel_masker (Masking whole-brain version) --> " "finished: ",
+            np.round(time.time() - start_time, 1),
+            "s",
+        )
+    )
 
-    print("%s%s%s" % ('node_gen_masking (Masking whole-brain version) --> '
-                      'finished: ',
-                      np.round(time.time() - start_time, 1), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "node_gen_masking (Masking whole-brain version) --> " "finished: ",
+            np.round(time.time() - start_time, 1),
+            "s",
+        )
+    )
 
     assert WB_coords is not None
     assert WB_labels is not None
@@ -384,29 +589,42 @@ def test_nodemaker_tools_masking_parlistfile_WB():
     assert WB_coords_masked is not None
 
 
-@pytest.mark.parametrize("atlas", ['coords_dosenbach_2010',
-                                   'coords_power_2011'])
+@pytest.mark.parametrize("atlas", ["coords_dosenbach_2010",
+                                   "coords_power_2011"])
 def test_nodemaker_tools_masking_coords_WB(atlas):
     """
     Test nodemaker_tools_masking_coords_WB functionality
     """
     # Set example inputs
-    roi = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
+    roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
     data_gen.generate_mni_space_img()[1].to_filename(roi)
     error = 2
 
     start_time = time.time()
     [WB_coords, _, _, WB_labels] = nodemaker.fetch_nilearn_atlas_coords(atlas)
-    print("%s%s%s" % ('fetch_nilearn_atlas_coords (Masking whole-brain '
-                      'coords version) --> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+    print(
+        "%s%s%s"
+        % (
+            "fetch_nilearn_atlas_coords (Masking whole-brain "
+            "coords version) --> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     start_time = time.time()
     [WB_coords_masked, WB_labels_masked] = nodemaker.coords_masker(
-        roi, WB_coords, WB_labels, error)
-    print("%s%s%s" % ('coords_masker (Masking whole-brain coords version) '
-                      '--> finished: ',
-                      str(np.round(time.time() - start_time, 1)), 's'))
+        roi, WB_coords, WB_labels, error
+    )
+    print(
+        "%s%s%s"
+        % (
+            "coords_masker (Masking whole-brain coords version) "
+            "--> finished: ",
+            str(np.round(time.time() - start_time, 1)),
+            "s",
+        )
+    )
 
     assert WB_coords is not None
     assert WB_coords is not None
@@ -420,11 +638,12 @@ def test_create_spherical_roi_volumes():
     Test create_spherical_roi_volumes functionality
     """
     node_size = 2
-    template_mask = pkg_resources.resource_filename("pynets",
-                                                    "templates/MNI152_T1_"
-                                                    "brain_mask_2mm.nii.gz")
+    template_mask = pkg_resources.resource_filename(
+        "pynets", "templates/MNI152_T1_" "brain_mask_2mm.nii.gz"
+    )
     [parcel_list, _, _, _] = nodemaker.create_spherical_roi_volumes(
-        node_size, [(0, 0, 0), (5, 5, 5)], template_mask)
+        node_size, [(0, 0, 0), (5, 5, 5)], template_mask
+    )
     assert len([i for i in parcel_list]) > 0
 
 
@@ -432,9 +651,9 @@ def test_get_sphere():
     """
     Test get_sphere functionality
     """
-    img_file = pkg_resources.resource_filename("pynets",
-                                               f"templates/MNI152_T1_brain_"
-                                               f"2mm.nii.gz")
+    img_file = pkg_resources.resource_filename(
+        "pynets", f"templates/MNI152_T1_brain_" f"2mm.nii.gz"
+    )
     img = nib.load(img_file)
     r = 4
     vox_dims = (2.0, 2.0, 2.0)
@@ -454,20 +673,21 @@ def test_parcel_naming():
     """
     coords = [(0, 0, 0), (-5, -5, -5), (5, 5, 5), (-10, -10, -10),
               (10, 10, 10)]
-    labels = nodemaker.parcel_naming(coords, vox_size='2mm')
+    labels = nodemaker.parcel_naming(coords, vox_size="2mm")
     assert len(coords) == len(labels)
 
 
 def test_enforce_hem_distinct_consecutive_labels():
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_"
-                                           "cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
     shutil.copy2(parlistfile, f"{dir_path}/{os.path.basename(parlistfile)}")
     parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}"
 
-    parcellation = nodemaker.enforce_hem_distinct_consecutive_labels(parlistfile)[0]
+    parcellation = nodemaker.enforce_hem_distinct_consecutive_labels(
+        parlistfile)[0]
     parcellation_img = nib.load(parcellation)
     parcels_parcellation = len(np.unique(parcellation_img.get_fdata())) - 1
     assert parcels_parcellation == 354
@@ -476,9 +696,9 @@ def test_enforce_hem_distinct_consecutive_labels():
 def test_drop_coords_labels_from_restricted_parcellation():
     from nipype.utils.filemanip import copyfile
 
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_"
-                                           "cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
 
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
@@ -486,58 +706,61 @@ def test_drop_coords_labels_from_restricted_parcellation():
     parlistfile = f"{dir_path}/{os.path.basename(parlistfile)}"
 
     [coords, _, _, label_intensities] = \
-        nodemaker.get_names_and_coords_of_parcels(parlistfile)
+        nodemaker.get_names_and_coords_of_parcels(parlistfile
+    )
 
-    labels = np.arange(len(coords) +
-                       1)[np.arange(len(coords) + 1) != 0].tolist()
+    labels = np.arange(len(coords) + 1)[np.arange(len(coords) + 1) != 0
+                                        ].tolist()
     labs = list(zip(labels, label_intensities))
-    [parcellation_okay, cleaned_coords, cleaned_labels] = \
-        nodemaker.drop_coords_labels_from_restricted_parcellation(parlistfile,
-                                                                  coords,
-                                                                  labs)
+    [
+        parcellation_okay,
+        cleaned_coords,
+        cleaned_labels,
+    ] = nodemaker.drop_coords_labels_from_restricted_parcellation(
+        parlistfile, coords, labs
+    )
 
     parcellation_okay_img = nib.load(parcellation_okay)
-    intensities_ok = list(np.unique(
-        np.asarray(
-            parcellation_okay_img.dataobj).astype("int"))[1:]
+    intensities_ok = list(
+        np.unique(np.asarray(parcellation_okay_img.dataobj).astype("int"))[1:]
     )
 
     assert len(cleaned_coords) == len(cleaned_labels) == len(intensities_ok)
 
     parlist_img = nib.load(parlistfile)
     parlist_img_data = parlist_img.get_fdata()
-    parlist_img_data[np.where(parlist_img_data==10)] = 0
-    par_tmp = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz')
+    parlist_img_data[np.where(parlist_img_data == 10)] = 0
+    par_tmp = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz")
     nib.save(nib.Nifti1Image(parlist_img_data, affine=parlist_img.affine),
              par_tmp.name)
-    [parcellation_okay, cleaned_coords, cleaned_labels] = \
-        nodemaker.drop_coords_labels_from_restricted_parcellation(parlistfile,
-                                                                  coords,
-                                                                  labs)
+    [
+        parcellation_okay,
+        cleaned_coords,
+        cleaned_labels,
+    ] = nodemaker.drop_coords_labels_from_restricted_parcellation(
+        parlistfile, coords, labs
+    )
     parcellation_okay_img = nib.load(parcellation_okay)
-    intensities_ok = list(np.unique(
-        np.asarray(
-            parcellation_okay_img.dataobj).astype("int"))[1:]
+    intensities_ok = list(
+        np.unique(np.asarray(parcellation_okay_img.dataobj).astype("int"))[1:]
     )
 
     assert len(cleaned_coords) == len(cleaned_labels) == len(intensities_ok)
 
     bad_coords = np.delete(coords, 30, axis=0)
     del labs[-30]
-    par_tmp2 = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
-    copyfile(
-        parlistfile,
-        par_tmp2,
-        copy=True,
-        use_hardlink=False)
-    [parcellation_mod, cleaned_coords, cleaned_labels] = \
-        nodemaker.drop_coords_labels_from_restricted_parcellation(par_tmp2,
-                                                                  bad_coords,
-                                                                  labs)
+    par_tmp2 = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
+    copyfile(parlistfile, par_tmp2, copy=True, use_hardlink=False)
+    [
+        parcellation_mod,
+        cleaned_coords,
+        cleaned_labels,
+    ] = nodemaker.drop_coords_labels_from_restricted_parcellation(
+        par_tmp2, bad_coords, labs
+    )
     parcellation_mod_img = nib.load(parcellation_mod)
-    intensities_ok = list(np.unique(
-        np.asarray(
-            parcellation_mod_img.dataobj).astype("int"))[1:]
+    intensities_ok = list(
+        np.unique(np.asarray(parcellation_mod_img.dataobj).astype("int"))[1:]
     )
 
     assert len(cleaned_coords) == len(cleaned_labels) == len(intensities_ok)
@@ -547,23 +770,23 @@ def test_mask_roi():
     """
     Test mask_roi functionality
     """
-    mask = pkg_resources.resource_filename("pynets",
-                                           "templates/MNI152_T1_brain_mask_"
-                                           "2mm.nii.gz")
+    mask = pkg_resources.resource_filename(
+        "pynets", "templates/MNI152_T1_brain_mask_" "2mm.nii.gz"
+    )
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
-    func_file = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
+    func_file = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
     data_gen.generate_fake_fmri()[1].to_filename(func_file)
-    roi = tempfile.NamedTemporaryFile(mode='w+', suffix='.nii.gz').name
+    roi = tempfile.NamedTemporaryFile(mode="w+", suffix=".nii.gz").name
     data_gen.generate_mni_space_img()[1].to_filename(roi)
     roi_masked = nodemaker.mask_roi(dir_path, roi, mask, func_file)
     assert roi_masked is not None
 
 
 def test_drop_bad_ixs():
-    parlistfile = pkg_resources.resource_filename("pynets",
-                                           "templates/atlases/whole_brain_"
-                                           "cluster_labels_PCA200.nii.gz")
+    parlistfile = pkg_resources.resource_filename(
+        "pynets", "templates/atlases/whole_brain_cluster_labels_PCA200.nii.gz"
+    )
 
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path, exist_ok=True)
diff --git a/tests/test_plotting.py b/tests/test_plotting.py
index 64b83682..44c4777a 100755
--- a/tests/test_plotting.py
+++ b/tests/test_plotting.py
@@ -2,9 +2,6 @@
 # -*- coding: utf-8 -*-
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
-
 """
 import os
 import numpy as np
@@ -15,10 +12,9 @@
     import _pickle as pickle
 from pathlib import Path
 import pytest
-from pynets.plotting import plot_gen, plot_graphs
+from pynets.plotting import brain, adjacency
 import tempfile
 import logging
-import pkg_resources
 import networkx as nx
 
 logger = logging.getLogger(__name__)
@@ -48,8 +44,8 @@ def test_plot_conn_mat_nonet_no_mask(plotting_data):
     labels = plotting_data['labels']
 
     start_time = time.time()
-    plot_graphs.plot_conn_mat_func(conn_matrix, conn_model, atlas, dir_path,
-    ID, subnet, labels, roi, thr, node_size, smooth, hpass, extract_strategy)
+    adjacency.plot_conn_mat_func(conn_matrix, conn_model, atlas, dir_path,
+                                 ID, subnet, labels, roi, thr, node_size, smooth, hpass, extract_strategy)
     print("%s%s%s" % ('plot_conn_mat_func --> finished: ',
     str(np.round(time.time() - start_time, 1)), 's'))
 
@@ -78,8 +74,8 @@ def test_plot_conn_mat_nonet_mask(plotting_data):
     labels = plotting_data['labels']
 
     start_time = time.time()
-    plot_graphs.plot_conn_mat_func(conn_matrix, conn_model, atlas, dir_path,
-    ID, subnet, labels, roi, thr, node_size, smooth, hpass, extract_strategy)
+    adjacency.plot_conn_mat_func(conn_matrix, conn_model, atlas, dir_path,
+                                 ID, subnet, labels, roi, thr, node_size, smooth, hpass, extract_strategy)
     print("%s%s%s" % ('plot_conn_mat_func (Masking version) --> finished: ',
     str(np.round(time.time() - start_time, 1)), 's'))
 
@@ -115,9 +111,9 @@ def test_plot_all_nonet_no_mask(plotting_data):
     coords = plotting_data['coords']
 
     start_time = time.time()
-    plot_gen.plot_all_func(conn_matrix, conn_model, atlas, dir_path, ID, subnet, labels, roi,
-                           coords, thr, node_size, edge_threshold, smooth, prune,
-                           parlistfile, norm, binary, hpass, extract_strategy)
+    brain.plot_all_func(conn_matrix, conn_model, atlas, dir_path, ID, subnet, labels, roi,
+                        coords, thr, node_size, edge_threshold, smooth, prune,
+                        parlistfile, norm, binary, hpass, extract_strategy)
     print("%s%s%s" % ('plot_all --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
 
     temp_dir.cleanup()
@@ -159,9 +155,9 @@ def test_plot_all_nonet_with_mask(plotting_data):
     labels = np.array(labels)
 
     start_time = time.time()
-    plot_gen.plot_all_func(conn_matrix, conn_model, atlas, dir_path, ID, subnet, labels, roi, coords, thr,
-                           node_size, edge_threshold, smooth, prune, parlistfile, norm, binary, hpass, extract_strategy,
-                           edge_color_override=True)
+    brain.plot_all_func(conn_matrix, conn_model, atlas, dir_path, ID, subnet, labels, roi, coords, thr,
+                        node_size, edge_threshold, smooth, prune, parlistfile, norm, binary, hpass, extract_strategy,
+                        edge_color_override=True)
     print("%s%s%s" % ('plot_all (Masking version) --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
 
     temp_dir.cleanup()
@@ -184,7 +180,7 @@ def test_plot_timeseries(plotting_data, subnet):
     labels = plotting_data['labels']
 
     start_time = time.time()
-    plot_gen.plot_timeseries(time_series, subnet, ID, dir_path, atlas, labels)
+    brain.plot_timeseries(time_series, subnet, ID, dir_path, atlas, labels)
     print("%s%s%s" % ('plot_timeseries --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
 
     temp_dir.cleanup()
@@ -194,7 +190,7 @@ def test_plot_timeseries(plotting_data, subnet):
 def test_plot_network_clusters(plotting_data, plot_overlaps):
     """ Test plotting subnet clusters"""
 
-    from pynets.stats.netstats import community_resolution_selection
+    from pynets.statistics.individual.algorithms import community_resolution_selection
 
     temp_file = tempfile.NamedTemporaryFile(mode='w+', prefix='figure', suffix='.png')
     fname = str(temp_file.name)
@@ -205,9 +201,9 @@ def test_plot_network_clusters(plotting_data, plot_overlaps):
     _, communities, _, _ = community_resolution_selection(G)
     plot_labels = True
 
-    plot_gen.plot_network_clusters(G, communities, fname,
-                                   plot_overlaps=plot_overlaps,
-                                   plot_labels=plot_labels)
+    brain.plot_network_clusters(G, communities, fname,
+                                plot_overlaps=plot_overlaps,
+                                plot_labels=plot_labels)
 
     assert os.path.isfile(fname)
 
@@ -232,8 +228,8 @@ def test_create_gb_palette(plotting_data, prune, node_cmap):
 
     color_theme = 'binary'
 
-    palette = plot_gen.create_gb_palette(conn_matrix, color_theme, coords, labels,
-                                         prune=prune, node_cmap=node_cmap)
+    palette = brain.create_gb_palette(conn_matrix, color_theme, coords, labels,
+                                      prune=prune, node_cmap=node_cmap)
     for param in palette:
         assert param is not None
 
@@ -250,7 +246,7 @@ def test_plot_conn_mat_rois_gt_100(plotting_data):
     labels = plotting_data['labels']
 
     start_time = time.time()
-    plot_graphs.plot_conn_mat(conn_matrix, labels, dir_path, cmap='Blues')
+    adjacency.plot_conn_mat(conn_matrix, labels, dir_path, cmap='Blues')
     print("%s%s%s" % ('plot_timeseries --> finished: ', str(np.round(time.time() - start_time, 1)), 's'))
 
     temp_dir.cleanup()
@@ -292,9 +288,9 @@ def test_plot_all_struct(plotting_data, roi):
     min_length = 10
     error_margin = 2
 
-    plot_gen.plot_all_struct(conn_matrix, conn_model, atlas, dir_path, ID, subnet, labels, roi,
-                             coords, thr, node_size, edge_threshold, prune, parcellation, target_samples,
-                             norm, binary, track_type, directget, min_length, error_margin)
+    brain.plot_all_struct(conn_matrix, conn_model, atlas, dir_path, ID, subnet, labels, roi,
+                          coords, thr, node_size, edge_threshold, prune, parcellation, target_samples,
+                          norm, binary, track_type, directget, min_length, error_margin)
 
     temp_dir.cleanup()
 
@@ -350,9 +346,9 @@ def test_plot_graph_measure_hists(nan):
         df = pd.read_csv(df_csv)
         df[df.columns[4]] = np.nan
         df.to_csv(f"{dir_name}/TEST.csv", index=False)
-        fig = plot_gen.plot_graph_measure_hists(f"{dir_name}/TEST.csv")
+        fig = brain.plot_graph_measure_hists(f"{dir_name}/TEST.csv")
     else:
-        fig = plot_gen.plot_graph_measure_hists(df_csv)
+        fig = brain.plot_graph_measure_hists(df_csv)
     assert fig is not None
     temp_dir.cleanup()
 
diff --git a/tests/test_refs.py b/tests/test_refs.py
index 5a1b6d4f..0e969d2b 100755
--- a/tests/test_refs.py
+++ b/tests/test_refs.py
@@ -2,7 +2,6 @@
 # -*- coding: utf-8 -*-
 """
 Created on Wed Dec 27 16:19:14 2017
-@authors: Derek Pisner & Ryan Hammonds
 """
 import nibabel as nib
 from pathlib import Path
diff --git a/tests/test_reg_utils.py b/tests/test_reg_utils.py
index 21eeba19..8a29c4d2 100644
--- a/tests/test_reg_utils.py
+++ b/tests/test_reg_utils.py
@@ -1,10 +1,8 @@
 #!/usr/bin/env python
 """
 Created on Monday July 29 16:19:14 2019
-
-@authors: Ryan Hammonds and Derek Pisner
-
 """
+import pytest
 import numpy as np
 from pynets.registration import utils
 import os
@@ -58,7 +56,8 @@ def test_applyxfm():
 
     ## First test: Apply xfm from test_align to orig anat img.
     inp = f"{anat_dir}/sub-003_T1w_brain.nii.gz"
-    ref = pkg_resources.resource_filename("pynets", f"templates/MNI152_T1_brain_2mm.nii.gz")
+    ref = pkg_resources.resource_filename(
+        "pynets", f"templates/MNI152_T1_brain_2mm.nii.gz")
     xfm = f"{anat_dir}/highres2standard.mat"
     aligned = f"{anat_dir}/highres2standard_2.nii.gz"
     utils.applyxfm(ref, inp, xfm, aligned, interp='trilinear', dof=6)
@@ -384,15 +383,17 @@ def test_make_median_b0():
 
     assert os.path.isfile(mean_file_out)
 
-
-def test_gen_mask():
+@pytest.mark.parametrize("mask_type", ['Normal', 'Empty'])
+def test_gen_mask(mask_type):
     base_dir = str(Path(__file__).parent/"examples")
     t1w_head = f"{base_dir}/003/anat/sub-003_T1w.nii.gz"
     t1w_brain = f"{base_dir}/003/anat/t1w_brain.nii.gz"
-    mask = f"{base_dir}/003/anat/t1w_brain_mask.nii.gz"
+    if(mask_type == 'Normal'):
+        mask = f"{base_dir}/003/anat/t1w_brain_mask.nii.gz"
+    elif(mask_type == 'Empty'):
+        mask = None
     utils.gen_mask(t1w_head, t1w_brain, mask)
 
-
 def test_deep_skull_strip():
     base_dir = str(Path(__file__).parent/"examples")
     t1w_head = f"{base_dir}/003/anat/sub-003_T1w.nii.gz"
diff --git a/tests/test_thresholding.py b/tests/test_thresholding.py
index 2503e109..840a5948 100755
--- a/tests/test_thresholding.py
+++ b/tests/test_thresholding.py
@@ -2,9 +2,6 @@
 # -*- coding: utf-8 -*-
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
-
 """
 import os
 import numpy as np
diff --git a/tests/test_track.py b/tests/test_track.py
index c9d4500c..5d35e95b 100644
--- a/tests/test_track.py
+++ b/tests/test_track.py
@@ -1,9 +1,6 @@
 #!/usr/bin/env python
 """
 Created on Thur July 18 20:19:14 2019
-
-@authors: Derek Pisner & Ryan Hammonds
-
 """
 import pytest
 try:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index c0ad3019..9aa55ac6 100755
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,8 +1,6 @@
 ﻿#!/usr/bin/env python
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
 """
 import numpy as np
 import os
@@ -484,7 +482,7 @@ def t_sleep(sec):
 @pytest.mark.parametrize("modality", ['func', 'dwi'])
 def test_build_mp_dict(modality):
     import tempfile
-    from pynets.stats.utils import build_mp_dict
+    from pynets.statistics.utils import build_mp_dict
     dir_path = str(tempfile.TemporaryDirectory().name)
     os.makedirs(dir_path)
     base_dir = str(Path(__file__).parent / "examples")
diff --git a/tests/test_workflows_dwi.py b/tests/test_workflows_dwi.py
index 5242906f..0a512359 100755
--- a/tests/test_workflows_dwi.py
+++ b/tests/test_workflows_dwi.py
@@ -1,8 +1,6 @@
 ﻿#!/usr/bin/env python
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
 """
 from pathlib import Path
 try:
diff --git a/tests/test_workflows_func.py b/tests/test_workflows_func.py
index c02685c7..2d5662b8 100755
--- a/tests/test_workflows_func.py
+++ b/tests/test_workflows_func.py
@@ -1,8 +1,6 @@
 ﻿#!/usr/bin/env python
 """
 Created on Wed Dec 27 16:19:14 2017
-
-@authors: Derek Pisner & Ryan Hammonds
 """
 from pathlib import Path
 try: