Repository: DiamondLightSource/httomo (language: python)

Issue:
Add pull request template
With a lack of CI for the moment, it's easy for developers to forget to run all tests before merging a PR.

One thing that could be helpful is having a checklist of some sort for each PR that must be complete before a merge is possible. One item on the checklist could state that all tests must pass before the PR can be considered for merging.

More info about pull request templates can be found at https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository
CI changes, precommit hook
pre-commit hook added and applied to files globally. #263 


Discussion / hints:

Fix patch:
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100755
index 000000000..14e642018
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,7 @@
+Fixes #ISSUE
+
+## Checklist
+
+- [ ] I have added tests that prove my fix is effective or that my feature works
+- [ ] New and existing unit tests pass locally with my changes
+- [ ] I have made corresponding changes to the documentation
diff --git a/.github/workflows/checker_action.yml b/.github/workflows/checker_action.yml
new file mode 100644
index 000000000..1ee0dc3c8
--- /dev/null
+++ b/.github/workflows/checker_action.yml
@@ -0,0 +1,12 @@
+name: 'PR Tasks Completed Check'
+on: 
+  pull_request:
+    types: [opened, edited]
+
+jobs:
+  task-check:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: kentaro-m/task-completed-checker-action@v0.1.2
+        with:
+          repo-token: "${{ secrets.GITHUB_TOKEN }}"
\ No newline at end of file
diff --git a/.github/workflows/conda_upload.yml b/.github/workflows/conda_upload.yml
index 05be7d43a..c646e454f 100644
--- a/.github/workflows/conda_upload.yml
+++ b/.github/workflows/conda_upload.yml
@@ -1,4 +1,4 @@
-name: httomo conda upload
+name: HTTomo version build
 
 # Run the workflow whenever a tag beginning with `v` is pushed to any branch
 on:
@@ -7,8 +7,8 @@ on:
       - v*
 
 jobs:
-  build-linux:
-    runs-on: ubuntu-20.04
+  conda-build-upload:
+    runs-on: ubuntu-latest
 
     defaults:
       run:
@@ -16,7 +16,7 @@ jobs:
 
     steps:
       - name: Checkout repository code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
           ref: ${{ github.ref_name }}
@@ -35,7 +35,7 @@ jobs:
           $CONDA/bin/conda install -c conda-forge conda-build
           $CONDA/bin/conda install -c conda-forge anaconda-client
           $CONDA/bin/conda update conda
-          $CONDA/bin/conda update conda-build          
+          $CONDA/bin/conda update conda-build
           $CONDA/bin/conda list
 
       - name: Decrypt a secret
diff --git a/.github/workflows/doc_conda.yml b/.github/workflows/doc_conda.yml
index 40c05ebea..275d074fb 100644
--- a/.github/workflows/doc_conda.yml
+++ b/.github/workflows/doc_conda.yml
@@ -1,4 +1,4 @@
-name: HTTomo doc test
+name: HTTomo docs
 
 on:
   workflow_dispatch:
@@ -10,19 +10,19 @@ on:
       - main
 
 jobs:
-  build-linux:
+  build-docs-publish:
     runs-on: ubuntu-latest
     defaults:
       run:
         shell: bash -el {0}
     steps:
         - name: Checkout repository code
-          uses: actions/checkout@v3
+          uses: actions/checkout@v4
 
-        - name: Setup Python 3.9
-          uses: actions/setup-python@v3
+        - name: Setup Python 3.10
+          uses: actions/setup-python@v4
           with:
-            python-version: 3.9
+            python-version: '3.10'
 
         - name: httomo-docs
           uses: conda-incubator/setup-miniconda@v2
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index c8fc22429..fed315149 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,4 +1,4 @@
-name: Python linters
+name: Lint
 
 on:
   push:
@@ -9,24 +9,10 @@ on:
       - main
 
 jobs:
-  run-linters:
-    name: Run linters
+  lint:
     runs-on: ubuntu-latest
-
     steps:
       - name: Checkout repository code
-        uses: actions/checkout@v2
-
-      # setup Python 3.9
-      - name: Setup Python 3.9
-        uses: actions/setup-python@v2
-        with:
-          python-version: 3.9
-
-      - name: Install Python dependencies
-        run: pip install black
-
-      - name: Run Black linter
-        uses: wearerequired/lint-action@v2
-        with:
-          black: true
+        uses: actions/checkout@v4
+      - name: Run black
+        uses: psf/black@stable
diff --git a/.github/workflows/main_weekly_build.yml b/.github/workflows/main_weekly_build.yml
new file mode 100644
index 000000000..af792fa0a
--- /dev/null
+++ b/.github/workflows/main_weekly_build.yml
@@ -0,0 +1,89 @@
+name: HTTomo main build (weekly)
+
+on:
+  schedule:
+    - cron: '0 0 * * 1' # At 00:00 on Monday
+
+jobs:
+
+  install-httomo-main-test-iris:
+    runs-on: iris-gpu
+    container:
+      image: nvidia/cuda:11.6.2-devel-ubi8
+      env:
+        NVIDIA_VISIBLE_DEVICES: ${{ env.NVIDIA_VISIBLE_DEVICES }}
+
+    defaults:
+      run:
+        shell: bash -l {0}
+
+    steps:
+      - name: Checkout repository code
+        uses: actions/checkout@v4
+
+      - name: Create conda environment
+        uses: mamba-org/setup-micromamba@v1
+        with:
+          environment-file: conda/environment.yml
+          environment-name: httomo
+          post-cleanup: 'all'
+          init-shell: bash
+          
+      - name: Install httomo-main
+        run: |
+          micromamba activate httomo
+          pip install .[dev]
+          micromamba list
+                
+      - name: Generate yaml templates
+        run: |
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/httomo_modules.yaml -o ./httomo/yaml_templates/httomo
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/tomopy/tomopy_modules.yaml -o ./httomo/yaml_templates/tomopy
+          python ./scripts/yaml_unsupported_tomopy_remove.py -t ./httomo/yaml_templates/tomopy -l ./httomo/methods_database/packages/external/tomopy/tomopy.yaml
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/httomolib/httomolib_modules.yaml -o ./httomo/yaml_templates/httomolib
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/httomolibgpu/httomolibgpu_modules.yaml -o ./httomo/yaml_templates/httomolibgpu
+
+      - name: Run tests
+        run: |
+          pytest tests/
+
+  conda-upload:
+    runs-on: ubuntu-latest
+
+    defaults:
+      run:
+        shell: bash -l {0}
+
+    steps:
+      - name: Checkout repository code
+        uses: actions/checkout@v4
+        with:
+          ref: "main"
+          fetch-depth: 0
+
+      # setup Python 3.10
+      - name: Setup Python 3.10
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.10'
+
+      - name: Install dependencies with Conda
+        run: |
+          $CONDA/bin/conda install -c conda-forge conda-build
+          $CONDA/bin/conda install -c conda-forge anaconda-client
+          $CONDA/bin/conda update conda
+          $CONDA/bin/conda update conda-build
+          $CONDA/bin/conda list
+
+      - name: Decrypt a secret
+        run: ./.scripts/decrypt_secret.sh
+        env:
+          LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
+
+      - name: Build and upload the package to httomo conda cloud
+        env:
+          LABEL: main
+        run: |
+          chmod +x ./.scripts/conda_upload.sh
+          ./.scripts/conda_upload.sh
+
diff --git a/.github/workflows/nightly_build.yml b/.github/workflows/nightly_build.yml
deleted file mode 100644
index 2c710a44d..000000000
--- a/.github/workflows/nightly_build.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-name: httomo nightly dev conda package build + upload
-
-on:
-  schedule:
-    # Run at midnight every day
-    - cron: '0 0 * * *'
-
-jobs:
-  build-linux:
-    runs-on: ubuntu-20.04
-
-    defaults:
-      run:
-        shell: bash -l {0}
-
-    steps:
-      - name: Checkout repository code
-        uses: actions/checkout@v3
-        with:
-          ref: "main"
-          fetch-depth: 0
-
-      # setup Python 3.9
-      - name: Setup Python 3.9
-        uses: actions/setup-python@v2
-        with:
-          python-version: 3.9
-          
-      - name: Install dependencies with Conda
-        run: |
-          $CONDA/bin/conda install -c conda-forge conda-build
-          $CONDA/bin/conda install -c conda-forge anaconda-client
-          $CONDA/bin/conda update conda
-          $CONDA/bin/conda update conda-build
-          $CONDA/bin/conda list          
-
-      - name: Decrypt a secret
-        run: ./.scripts/decrypt_secret.sh
-        env:
-          LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
-
-      - name: Build and upload the package to httomo conda cloud
-        env:
-          LABEL: dev
-        run: |
-          chmod +x ./.scripts/conda_upload.sh
-          ./.scripts/conda_upload.sh
-
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 000000000..5b7ada818
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,33 @@
+repos:
+- repo: https://github.com/psf/black
+  rev: 23.12.1
+  hooks:
+    - id: black
+      language_version: python3
+      exclude: ^docs/source/examples/
+- repo: https://github.com/pre-commit/pre-commit-hooks
+  rev: v4.5.0
+  hooks:
+    - id: trailing-whitespace  # This hook trims trailing whitespace.
+    - id: check-docstring-first  # Checks a common error of defining a docstring after code.
+    - id: check-merge-conflict  # Check for files that contain merge conflict strings.
+    - id: detect-private-key  # Detects the presence of private keys.
+    - id: check-symlinks
+    - id: check-toml
+- repo: https://github.com/pre-commit/pygrep-hooks
+  rev: v1.10.0
+  hooks:
+    - id: python-use-type-annotations
+- repo: https://github.com/kynan/nbstripout
+  rev: 0.6.1
+  hooks:
+    - id: nbstripout
+      exclude: ^docs/source/examples/
+-   repo: https://github.com/asottile/blacken-docs
+    rev: 1.16.0
+    hooks:
+    - id: blacken-docs
+- repo: https://github.com/nbQA-dev/nbQA
+  rev: 1.7.1
+  hooks:
+    - id: nbqa-black
\ No newline at end of file
diff --git a/README.rst b/README.rst
index 2e5aa9eae..b04b9ce03 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,13 @@
 HTTomo (High Throughput Tomography pipeline)
 *******************************************************
 
-HTTomo is a user interface (UI) written in Python for fast big data processing using MPI protocols. 
+HTTomo is a user interface (UI) written in Python for fast big data processing using MPI protocols.
 It orchestrates I/O data operations and enables processing on a CPU and/or a GPU. HTTomo utilises other libraries, such as `TomoPy <https://tomopy.readthedocs.io>`_ and `HTTomolibgpu <https://github.com/DiamondLightSource/httomolibgpu>`_
 as backends for data processing. The methods from the libraries are exposed through YAML templates to enable fast task programming.
 
 Installation
 ============
-See detailed instructions for `installation <https://diamondlightsource.github.io/httomo/howto/installation.html>`_ . 
+See detailed instructions for `installation <https://diamondlightsource.github.io/httomo/howto/installation.html>`_ .
 
 Documentation
 ==============
diff --git a/conda/environment-cpu.yml b/conda/environment-cpu.yml
index e1f2883b9..f7aac6987 100644
--- a/conda/environment-cpu.yml
+++ b/conda/environment-cpu.yml
@@ -11,10 +11,10 @@ dependencies:
   - conda-forge::h5py=*=*mpi_openmpi*
   - conda-forge::pyyaml
   - conda-forge::numpy<=1.25
-  - conda-forge::python 
+  - conda-forge::python
   - conda-forge::numexpr>=2.8.4
   - anaconda::ipython
-  - anaconda::loguru  
+  - anaconda::loguru
   - conda-forge::graypy
   - conda-forge::plumbum
   - conda-forge::tqdm
diff --git a/conda/recipe/meta.yaml b/conda/recipe/meta.yaml
index f807ae3ce..068ce3713 100644
--- a/conda/recipe/meta.yaml
+++ b/conda/recipe/meta.yaml
@@ -27,7 +27,7 @@ test:
     - tests/*
   commands:
     - pip check
-    - pytest 
+    - pytest
 requirements:
   build:
     - h5py=*=*mpi_openmpi*
diff --git a/docs/source/backends/list.rst b/docs/source/backends/list.rst
index 37f661e22..21e81a640 100644
--- a/docs/source/backends/list.rst
+++ b/docs/source/backends/list.rst
@@ -3,36 +3,36 @@
 Supported backends
 ==============================
 
-HTTomo currently supports several software packages that are used as 
-backends to perform data processing and reconstruction. The list of 
+HTTomo currently supports several software packages that are used as
+backends to perform data processing and reconstruction. The list of
 the external packages will be growing in future to meet users' needs.
 
-If the package has a modular structure with an easy access to every 
-method, for example as `TomoPy <https://tomopy.readthedocs.io>`_ 
-software or `scikit <https://scikit-image.org/>`_ library, then the 
-integration process is straightforward. 
+If the package has a modular structure with an easy access to every
+method, for example as `TomoPy <https://tomopy.readthedocs.io>`_
+software or `scikit <https://scikit-image.org/>`_ library, then the
+integration process is straightforward.
 The required YAML template can be generated by using :ref:`utilities_yamlgenerator`,
-or manually, and used with HTTomo. More complicated in structure packages would need 
-an additional wrapping, see for instance the reconstruction methods in 
-the `HTTomolibgpu <https://github.com/DiamondLightSource/httomolibgpu/blob/main/httomolibgpu/recon/algorithm.py>`_ library. 
+or manually, and used with HTTomo. More complicated in structure packages would need
+an additional wrapping, see for instance the reconstruction methods in
+the `HTTomolibgpu <https://github.com/DiamondLightSource/httomolibgpu/blob/main/httomolibgpu/recon/algorithm.py>`_ library.
 
 Please see the provided list of :ref:`reference_templates`.
 
 TomoPy software (CPU)
 ---------------------
-`TomoPy <https://tomopy.readthedocs.io>`_ is an open-source Python package for 
-tomographic data processing and image reconstruction developed at 
-`The Advanced Photon Source <https://www.aps.anl.gov/>`_ in Illinois, USA. 
-The project is active since 2013 and it gained a `large audience <https://github.com/tomopy/tomopy>`_ 
+`TomoPy <https://tomopy.readthedocs.io>`_ is an open-source Python package for
+tomographic data processing and image reconstruction developed at
+`The Advanced Photon Source <https://www.aps.anl.gov/>`_ in Illinois, USA.
+The project is active since 2013 and it gained a `large audience <https://github.com/tomopy/tomopy>`_
 of users and contributors across tomographic imaging community.
 
-* TomoPy is an open-source package in Python and C for data processing and reconstruction. TomoPy is mostly a CPU processing library and in HTTomo we expose the CPU modules only. 
+* TomoPy is an open-source package in Python and C for data processing and reconstruction. TomoPy is mostly a CPU processing library and in HTTomo we expose the CPU modules only.
 * It is a CPU-multithreaded package. HTTomo controls parallelisation through MPI on a higher level and also supports local CPU multithreading from TomoPy, for every MPI process.
 * TomoPy is a library of stand-alone methods which can be easily integrated into HTTomo. Notably not all TomoPy methods are integrated in HTTomo because of the I/O nature of some modules. Please see the list of available TomoPy :ref:`reference_templates`.
 
 HTTomolibgpu library (GPU)
 --------------------------
-`HTTomolibgpu <https://github.com/DiamondLightSource/httomolibgpu>`_ library is developed at `Diamond Light source  <https://www.diamond.ac.uk/>`_ 
+`HTTomolibgpu <https://github.com/DiamondLightSource/httomolibgpu>`_ library is developed at `Diamond Light source  <https://www.diamond.ac.uk/>`_
 by Data Analysis Group to work together with the HTTomo software.
 
 * HTTomolibgpu is a Python library of GPU accelerated methods written using `CuPy <https://cupy.dev/>`_ API and CUDA language.
diff --git a/docs/source/backends/templates.rst b/docs/source/backends/templates.rst
index b93a0e62f..987d653a4 100644
--- a/docs/source/backends/templates.rst
+++ b/docs/source/backends/templates.rst
@@ -5,9 +5,9 @@ Methods YAML Templates
 ======================
 
 This section contains YAML templates from :ref:`backends_list`. These are ready-to-use templates can be either copy-pasted or
-downloaded in order to build your pipeline. 
+downloaded in order to build your pipeline.
 
-.. note:: When you click on a module you can find a link to the API of that module. This is where the description of the method and its arguments can be found. 
+.. note:: When you click on a module you can find a link to the API of that module. This is where the description of the method and its arguments can be found.
 
 
 TomoPy Modules
@@ -23,7 +23,7 @@ v.1.15
 .. _reference_templates_httomolibgpu:
 
 HTTomolibgpu Modules
--------------------- 
+--------------------
 v.2.0
 '''''
 
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 65cc56d8e..10c8d4afe 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -1,7 +1,25 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# ---------------------------------------------------------------------------
+# Copyright 2023 Diamond Light Source Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ecpress or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+# Created By  : Tomography Team at DLS <scientificsoftware@diamond.ac.uk>
+
 # -- General configuration ------------------------------------------------
 # https://www.sphinx-doc.org/en/master/usage/configuration.html
 
-#!/usr/bin/env python
 import os
 import sys
 from datetime import date
@@ -88,14 +106,6 @@
 html_static_path = ["_static"]
 html_use_smartypants = True
 
-"""
-html_theme_options = {
-    "logo_only": True,
-    "display_version": False,
-    "githuburl": "https://github.com/DiamondLightSource/httomo",
-}
-"""
-
 html_theme_options = {
     "logo": {
         "image_dark": "_static/logo_dark.png",
diff --git a/docs/source/developers/how_to_contribute.rst b/docs/source/developers/how_to_contribute.rst
index b35dbe147..0db79dfad 100644
--- a/docs/source/developers/how_to_contribute.rst
+++ b/docs/source/developers/how_to_contribute.rst
@@ -3,34 +3,34 @@
 How to contribute
 *****************
 
-For those who are interested in contributing to HTTomo, we provide here steps to follow. All additional enquires can be left in  
-the issues section on `HTTomo's Github page <https://github.com/DiamondLightSource/httomo/issues>`_. 
+For those who are interested in contributing to HTTomo, we provide here steps to follow. All additional enquires can be left in
+the issues section on `HTTomo's Github page <https://github.com/DiamondLightSource/httomo/issues>`_.
 
 1. Write a new data processing method in Python.
-   
+
    One needs to write a method and make it accessible in either a separate library or integrating
-   it into the list of already :ref:`backends_list`. The latter option is preferred as some of the packages are 
+   it into the list of already :ref:`backends_list`. The latter option is preferred as some of the packages are
    maintained by HTTomo developers which will provide support during the integration.
 
 2. Expose the method in the library file in HTTomo
 
    Then one needs to expose that method to HTTomo by editing the :ref:`pl_library`. You would need to specify
    the main static descriptors of that method, such as, :code:`pattern`, :code:`implementation`, etc. If the implementation is :code:`cpu` only,
-   then :code:`memory_gpu` must be set to :code:`None`. However, if the method requires GPU, then you would need to provide more information so 
-   that HTTomo's framework would account for the memory use on the device. See :code:`HTTomolibgpu` library file for that. 
+   then :code:`memory_gpu` must be set to :code:`None`. However, if the method requires GPU, then you would need to provide more information so
+   that HTTomo's framework would account for the memory use on the device. See :code:`HTTomolibgpu` library file for that.
    In a simple case, one can calculate the memory directly by providing multipliers in the library file. When memory
    calculation is more complicated, one needs to add a Python script that does this calculation. See more in :ref:`developers_memorycalc`.
 
-3. Check the wrapper type 
+3. Check the wrapper type
 
-   Every method is executed by using :ref:`info_wrappers`. Check that the method fits the existing wrapper type, and if not, then possibly more work required 
-   to accommodate it. In most of the cases the method should fit the existing types. 
+   Every method is executed by using :ref:`info_wrappers`. Check that the method fits the existing wrapper type, and if not, then possibly more work required
+   to accommodate it. In most of the cases the method should fit the existing types.
 
 4. Generate the Yaml template
 
-   HTTomo's UI requires :ref:`reference_templates` to execute the created method. One can either construct that YAML template manually or employ 
+   HTTomo's UI requires :ref:`reference_templates` to execute the created method. One can either construct that YAML template manually or employ
    :ref:`utilities_yamlgenerator`.
 
 
-  
-  
+
+
diff --git a/docs/source/developers/memory_calculation.rst b/docs/source/developers/memory_calculation.rst
index 4a6d434be..85650c0b2 100644
--- a/docs/source/developers/memory_calculation.rst
+++ b/docs/source/developers/memory_calculation.rst
@@ -14,7 +14,7 @@ The ``calc_max_slices`` function must have the following signature::
 The ``httomo`` package will call this function, passing in the dimension along which it will slice
 (``0`` for projection, ``1`` for sinogram), the other dimensions of the data array shape,
 the data type for the input, and the available memory on the GPU for method execution.
-Additionally it passes all other parameters of the method in the ``kwargs`` argument, 
+Additionally it passes all other parameters of the method in the ``kwargs`` argument,
 which can be used by the function in case parameters determine the memory consumption.
 The function should calculate how many slices along the slicing dimension it can fit into the given memory.
 Further, it returns the output datatype of the method (given the input ``dtype`` argument),
@@ -33,7 +33,7 @@ it can fit, given the other two dimension sizes. For example:
     max_slices, outdtype = my_method.meta.calc_max_slices(0, (10, 20), np.float32(), 14450, **method_args)
 
 * The developer of the given method needs to provide this function implementation,
-  and it needs to calculate the maximum number of slices it can fit. 
+  and it needs to calculate the maximum number of slices it can fit.
 * Assuming that the method is very simple and does not need any local temporary memory,
   requiring only space for the input and output array, it could be implemented as follows::
 
@@ -57,12 +57,12 @@ it can fit, given the other two dimension sizes. For example:
   * ``input_mem_per_slice = 800``
   * ``output_mem_per_slice = 800``
   * => ``max_slices = 14450 // 1600 = 9``
-  
+
 
 Max Slices Tests
 ----------------
 
-In order to test that the slice calculation function is reflecting reality, each method has a 
+In order to test that the slice calculation function is reflecting reality, each method has a
 unit test implemented that verifies that the calculation is right (within bounds).
 That is, it tests that the estimated slices are between 80% and 100% of the actually used slices.
 These tests also help to keep the memory estimation functions in sync with the implementation.
@@ -72,12 +72,12 @@ The strategy for testing is the other way around:
 * We first run the actual method, given a specific data set, and record the maximum memory actually
   used by the method.
 * Then, retrospectively, we call the ``calc_max_slices`` estimator function and pass in this memory
-  as the ``available_memory`` argument. So we're asking the estimation function to assume that 
-  the memory available is the actually used memory in the method call. 
+  as the ``available_memory`` argument. So we're asking the estimation function to assume that
+  the memory available is the actually used memory in the method call.
 * The estimated number of slices should then be less or equal to the actual slices used earlier.
 * To make sure the function is not too conservative, we're checking that it returns at least 80%
   of the slices that actually fit
 
 
-  
-  
+
+
diff --git a/docs/source/explanation/faq.rst b/docs/source/explanation/faq.rst
index 160c13f91..9ae89ba9a 100644
--- a/docs/source/explanation/faq.rst
+++ b/docs/source/explanation/faq.rst
@@ -13,10 +13,10 @@ Frequently Asked Questions
     Template can be used to build a process list or a pipeline of methods. Normally the content of a template (a YAML file) is copied to build a process list file. Please see :ref:`howto_process_list`.
 
 .. dropdown:: Can I create a template?
-	
+
 	You can create a template manually if you want to run a method from the external software. See more on :ref:`backends_list`.
 
-.. dropdown:: How can I configure a multi-task pipeline? 
+.. dropdown:: How can I configure a multi-task pipeline?
 
 	The multi-task pipeline is build from the available :ref:`reference_templates` by stacking them together. Please see :ref:`howto_process_list`.
 
@@ -25,9 +25,9 @@ Frequently Asked Questions
 	Please see :ref:`howto_run`.
 
 .. dropdown:: I have a Python method, can it be used with HTTomo?
-    
+
     There is a high chance that it can be used. The method needs to be accessible in your Python environment and you will need a YAML template for it. See more on what kind of :ref:`backends_list` can be used with HTTomo. It is also recommended if you integrate your method in a library first. See :ref:`developers_content`.
-	
+
 .. dropdown:: How can I contribute to HTTomo?
 
 	You can contribute by adding new methods to :ref:`backends_list` or by contributing to the source base of the `HTTomo project <https://github.com/DiamondLightSource/httomo>`_.
@@ -55,17 +55,17 @@ Working from a workstation at Diamond Light Source
 .. dropdown:: How can I use HTTomo inside the terminal?
 
     1. Using the module system, ``module load`` allows you to obtain an access to the installed HTTomo at Diamond computing systems.
-    You can check which versions of HTTomo are installed with the command: :code:`module avail httomo`. You can either load a specific version 
-    with ``module load httomo/*httomo_version*`` or the default (recommended) version by executing: 
+    You can check which versions of HTTomo are installed with the command: :code:`module avail httomo`. You can either load a specific version
+    with ``module load httomo/*httomo_version*`` or the default (recommended) version by executing:
 
     .. code-block:: console
-        
+
         $ module load httomo
 
     This will add all of the related packages and files into your path, meaning
     that you will have an access to these packages from your loaded Python environment.
-    
-    2. Configure your pipeline using the templates as shown previously 
+
+    2. Configure your pipeline using the templates as shown previously
     and run HTTomo.
 
 
@@ -73,7 +73,7 @@ Working from a workstation at Diamond Light Source
 
     It is modifying the users environment, by including the path to certain
     environment modules. In case of HTTomo it enables a specific conda environment with Python.
-    
+
     You can read more about how module works at `modules.readthedocs.io <https://modules.readthedocs.io>`_
 
 .. dropdown:: What do I do if I have module loaded the wrong version of HTTomo?
@@ -81,6 +81,6 @@ Working from a workstation at Diamond Light Source
     You can use repeat the module command, replacing ``load`` with ``unload``
 
     .. code-block:: console
-        
+
         $ module unload httomo/*httomo_old_version* # unload old version first
         $ module load httomo/*httomo_version* # load the correct one
diff --git a/docs/source/explanation/process_list.rst b/docs/source/explanation/process_list.rst
index dab97ef74..e2ef78604 100644
--- a/docs/source/explanation/process_list.rst
+++ b/docs/source/explanation/process_list.rst
@@ -3,7 +3,7 @@
 What is a process list?
 ------------------------
 
-A process list is a YAML file (see :ref:`explanation_yaml`), which is required to execute the processing of data in HTTomo. The process list file consists of methods exposed as YAML templates stacked together 
-to form a **serially processed sequence of methods**. Each YAML template represents a standalone method or a loader which can be chained together with other templates to form a process list. 
+A process list is a YAML file (see :ref:`explanation_yaml`), which is required to execute the processing of data in HTTomo. The process list file consists of methods exposed as YAML templates stacked together
+to form a **serially processed sequence of methods**. Each YAML template represents a standalone method or a loader which can be chained together with other templates to form a process list.
 
 Please check how :ref:`howto_process_list`.
diff --git a/docs/source/explanation/templates.rst b/docs/source/explanation/templates.rst
index 2d2a20d0e..1fbd5aeae 100644
--- a/docs/source/explanation/templates.rst
+++ b/docs/source/explanation/templates.rst
@@ -3,7 +3,7 @@
 What is a template?
 ------------------------
 
-A YAML template (see :ref:`explanation_yaml`) is a textual interface to a method, which can be executed in HTTomo. 
+A YAML template (see :ref:`explanation_yaml`) is a textual interface to a method, which can be executed in HTTomo.
 The template provides a communication with a chosen method by setting its input/output entries and also additional parameters, if required.
 
 The combination of YAML templates results in a processing list, also called a pipeline. See more on :ref:`explanation_process_list`
@@ -11,7 +11,7 @@ The combination of YAML templates results in a processing list, also called a pi
 As a simple template example, let's consider the template for the median filter from the `TomoPy <https://tomopy.readthedocs.io/en/stable/api/tomopy.misc.corr.html#tomopy.misc.corr.median_filter3d>`_ package.
 
 .. code-block:: yaml
-    
+
     - method: median_filter3d
       module_path: tomopy.misc.corr
       parameters:
diff --git a/docs/source/howto/httomo_features/centering.rst b/docs/source/howto/httomo_features/centering.rst
index 30ee72e98..1fedc20b0 100644
--- a/docs/source/howto/httomo_features/centering.rst
+++ b/docs/source/howto/httomo_features/centering.rst
@@ -6,20 +6,20 @@ Centre of Rotation (CoR)
 
 What is it?
 ===========
-Identifying the optimal Centre of Rotation (CoR) parameter is an important 
-procedure to ensure the correctness of the reconstruction. It is crucial to find it 
+Identifying the optimal Centre of Rotation (CoR) parameter is an important
+procedure to ensure the correctness of the reconstruction. It is crucial to find it
 as precise as possible, as the incorrect value can lead to distortions in reconstructions, therefore making post processing and
-quantification invalid.  
+quantification invalid.
 
 The required CoR parameter places the object (a scanned sample) into a coordinate system of the scanning device to ensure
 that the object is centered and rotates around its axis in this system, see :numref:`fig_centerscheme`. This is essential for
 valid reconstruction as the back projection model assumes a central placing of a sample with respect to the detector's axis
 (usually horizontal for synchrotrons).
 
-The CoR estimation problem is also sometimes referred to the centered sinogram. 
-If the sinogram is not centered and there is an offset (`d` in :numref:`fig_centerscheme`), the reconstruction 
-will result in strong arching artefacts at the boundaries of reconstructed objects, see :numref:`fig_center_find`. 
-Further from the optimal CoR value (here `d=0`), one should expect more pronounced arching. 
+The CoR estimation problem is also sometimes referred to the centered sinogram.
+If the sinogram is not centered and there is an offset (`d` in :numref:`fig_centerscheme`), the reconstruction
+will result in strong arching artefacts at the boundaries of reconstructed objects, see :numref:`fig_center_find`.
+Further from the optimal CoR value (here `d=0`), one should expect more pronounced arching.
 Therefore the optimisation problem usually involves minimising the artefacts in the reconstructed images
 by varying the CoR value.
 
@@ -35,12 +35,12 @@ by varying the CoR value.
     :scale: 85 %
     :alt: Finding CoR
 
-    The reconstructions using different CoR values. Incorrectly centered sinogram results in strong arching artifacts on the boundaries of the reconstructed object. Note how the arching is being reduced when `d` is closer to the correct value. 
+    The reconstructions using different CoR values. Incorrectly centered sinogram results in strong arching artifacts on the boundaries of the reconstructed object. Note how the arching is being reduced when `d` is closer to the correct value.
 
 CoR in HTTomo
 =============
 
-The CoR parameter is present in any reconstruction template given as the :code:`center` parameter. It can be configured either 
+The CoR parameter is present in any reconstruction template given as the :code:`center` parameter. It can be configured either
 automatically (see :ref:`centering_auto`) or manually (see :ref:`centering_manual`).
 
 .. _centering_auto:
@@ -48,18 +48,18 @@ automatically (see :ref:`centering_auto`) or manually (see :ref:`centering_manua
 Auto-centering
 ===============
 
-There is a `variety <https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=center+of+rotation+tomography&btnG=>`_ of 
-methods to estimate CoR automatically. At DLS, we frequently use the centering method which 
-was developed by Nghia Vo and it relies on the Fourier analysis of a sinogram, see the `paper <https://opg.optica.org/directpdfaccess/a01b4da4-e3cf-474a-b428ca517599c609_297315/oe-22-16-19078.pdf?da=1&id=297315&seq=0&mobile=no>`_. 
-This method is implemented in both TomoPy and HTTomolibgpu libraries and 
-available for HTTomo as *find_center_vo* template, see :ref:`reference_templates`. 
+There is a `variety <https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=center+of+rotation+tomography&btnG=>`_ of
+methods to estimate CoR automatically. At DLS, we frequently use the centering method which
+was developed by Nghia Vo and it relies on the Fourier analysis of a sinogram, see the `paper <https://opg.optica.org/directpdfaccess/a01b4da4-e3cf-474a-b428ca517599c609_297315/oe-22-16-19078.pdf?da=1&id=297315&seq=0&mobile=no>`_.
+This method is implemented in both TomoPy and HTTomolibgpu libraries and
+available for HTTomo as *find_center_vo* template, see :ref:`reference_templates`.
 
-Here are the steps to enable the auto-centering and then use the estimated value in the reconstruction: 
+Here are the steps to enable the auto-centering and then use the estimated value in the reconstruction:
 
 1. The auto-centering method should be added to the process list before the reconstruction method.
 2. It is recommended to position any auto-centering method right after the loader, see :ref:`pl_conf_order`.
 3. The calculated CoR value with be stored in :ref:`side_output`.
-4. In the reconstruction module we refer to that value by placing the reference into the :code:`center` parameter. 
+4. In the reconstruction module we refer to that value by placing the reference into the :code:`center` parameter.
 
 .. code-block:: yaml
   :emphasize-lines: 13,17
@@ -85,7 +85,7 @@ Here are the steps to enable the auto-centering and then use the estimated value
       recon_size: null
       recon_mask_radius: null
 
-.. note:: When one auto-centering method fails it is recommended to try other available methods as they can still provide the correct or close to the correct CoR value. 
+.. note:: When one auto-centering method fails it is recommended to try other available methods as they can still provide the correct or close to the correct CoR value.
 
 .. _centering_manual:
 
@@ -97,7 +97,7 @@ In that case, it is recommended to find the center of rotation manually. :ref:`p
 
 To enable manual centering, one would need to do the following steps:
 
-1. Ensure that the auto centering estimation method is not in the process list (remove or comment it). 
+1. Ensure that the auto centering estimation method is not in the process list (remove or comment it).
 2. Modify the centre of rotation value :code:`center` in the reconstruction plugin by substituting a number instead of the reference to side outputs.
 
 
diff --git a/docs/source/howto/httomo_features/previewing.rst b/docs/source/howto/httomo_features/previewing.rst
index f6c6ad98b..b44f56eac 100644
--- a/docs/source/howto/httomo_features/previewing.rst
+++ b/docs/source/howto/httomo_features/previewing.rst
@@ -4,7 +4,7 @@
 Previewing
 ^^^^^^^^^^
 
-Previewing is the way to change the dimensions of the input data by reducing them. 
+Previewing is the way to change the dimensions of the input data by reducing them.
 It also can be interpreted as a data cropping or data slicing operation.
 
 Reduction of the input data is often done to remove unnecessary/useless
@@ -29,7 +29,7 @@ a brief explanation is given on how to use the :code:`preview` parameter in the
     :scale: 55 %
     :alt: 3D data
 
-    3D projection data and their axes 
+    3D projection data and their axes
 
 
 Structure of the :code:`preview` parameter value
@@ -68,7 +68,7 @@ previewing is disabled in this case.
 Enabling data preview
 =====================
 
-In order to change the input data dimensions and accelerate the processing 
+In order to change the input data dimensions and accelerate the processing
 pipeline, one can do two of the following operations.
 
 .. note:: Although this is optional, by doing this the size of the reconstructed
@@ -87,7 +87,7 @@ Before cropping |pic1| and after |pic2|
 
 
 1. Reduce the size of the vertical dimension (detector- `Y`) by removing blank regions in your data (top and bottom cropping),
-   see :numref:`fig_dimsdataY`. The blank areas, if any, can be established by looking through the sequence of raw projections. 
+   see :numref:`fig_dimsdataY`. The blank areas, if any, can be established by looking through the sequence of raw projections.
 
    .. code-block:: yaml
 
@@ -109,10 +109,10 @@ Before cropping |pic1| and after |pic2|
 
 2. Reduce the size of the horizontal dimension (detector- `X`) by removing blank regions in your data (cropping the left and right sides),
    see :numref:`fig_dimsdataX`.
-   
+
    .. warning::
     Please be aware that cropping this dimension can create issues with the automatic centering
-    and potentially lead to reconstruction artefacts, especially if iterative methods are used. 
+    and potentially lead to reconstruction artefacts, especially if iterative methods are used.
     It is general practice to be more conservative with the cropping of the `X`
     detector dimension.
 
@@ -124,7 +124,7 @@ Before cropping |pic1| and after |pic2|
            stop: 2000
 
    In Python this will be interpreted as :code:`[:,:,100:2000]`.
-    
+
 .. _fig_dimsdataX:
 .. figure::  ../../_static/preview/dims_prevX.svg
     :scale: 55 %
diff --git a/docs/source/howto/installation.rst b/docs/source/howto/installation.rst
index af723f2d8..6aaf83c1a 100644
--- a/docs/source/howto/installation.rst
+++ b/docs/source/howto/installation.rst
@@ -4,7 +4,7 @@ Installation Guide
 Install HTTomo as a pre-built conda Python package
 ==================================================
 
-This installation is preferable as it should take care all of dependencies including :ref:`backends_list` by getting them from the dedicated anaconda channel. 
+This installation is preferable as it should take care all of dependencies including :ref:`backends_list` by getting them from the dedicated anaconda channel.
 
 .. code-block:: console
 
@@ -14,14 +14,14 @@ This installation is preferable as it should take care all of dependencies inclu
 Install as a Python module
 ===========================
 
-If installation above for some reason is not working for you, then the best way to install HTTomo is to create conda environment first and then 
-`pip install` HTTomo into it. You will need to `git clone` HTTomo repository to your disk first.  Use `environment.yml` file to install 
+If installation above for some reason is not working for you, then the best way to install HTTomo is to create conda environment first and then
+`pip install` HTTomo into it. You will need to `git clone` HTTomo repository to your disk first.  Use `environment.yml` file to install
 the GPU-supported HTTomo. For CPU-only version, please use `environment-cpu.yml` instead.
 
 .. code-block:: console
-    
+
    $ git clone git@github.com:DiamondLightSource/HTTomo.git # clone the repo
-   $ conda env create --name httomo --file conda/environment.yml # install dependencies for GPU version   
+   $ conda env create --name httomo --file conda/environment.yml # install dependencies for GPU version
    $ conda activate httomo # activate environment
    $ pip install . # Install the module
 
@@ -29,7 +29,7 @@ Setup HTTomo development environment:
 ======================================================
 .. code-block:: console
 
-   $ pip install -e .[dev] # development mode 
+   $ pip install -e .[dev] # development mode
 
 Build HTTomo as a conda Python package
 ======================================================
diff --git a/docs/source/howto/interpret_logger.rst b/docs/source/howto/interpret_logger.rst
index 784fa0b0f..4175662f7 100644
--- a/docs/source/howto/interpret_logger.rst
+++ b/docs/source/howto/interpret_logger.rst
@@ -3,41 +3,41 @@
 Interpret Log File
 ======================
 
-This section contains information on how to interpret the log file created by HTTomo. 
+This section contains information on how to interpret the log file created by HTTomo.
 
-HTTomo uses :code:`loguru` software to unify and simplify the logging system. During the job execution, the concise information 
-goes to the terminal (see :numref:`fig_log`) and also to the :code:`user.log` file. More verbose information, that is usually 
-needed to debug the run, is saved into the :code:`debug.log` file. Let us explain few main elements of the :code:`user.log` 
-file and also stdout. 
+HTTomo uses :code:`loguru` software to unify and simplify the logging system. During the job execution, the concise information
+goes to the terminal (see :numref:`fig_log`) and also to the :code:`user.log` file. More verbose information, that is usually
+needed to debug the run, is saved into the :code:`debug.log` file. Let us explain few main elements of the :code:`user.log`
+file and also stdout.
 
 .. _fig_log:
 .. figure::  ../_static/log/log_screenshot.png
     :scale: 40 %
     :alt: HTTomo log screenshot
 
-    The screenshot of the terminal output (AKA stdout) which also goes into the :code:`user.log` file. 
+    The screenshot of the terminal output (AKA stdout) which also goes into the :code:`user.log` file.
 
 
 * :code:`Pipeline has been separated into N sections`
-   This means that `N` :ref:`info_sections` created for this pipeline and each section contains a certain amount of methods grouped together to work on :ref:`blocks_data`. The progress can be seen in every 
+   This means that `N` :ref:`info_sections` created for this pipeline and each section contains a certain amount of methods grouped together to work on :ref:`blocks_data`. The progress can be seen in every
    section processing all of the input data divided into :ref:`chunks_data` and :ref:`blocks_data`, before continue to the next section.
 
 * :code:`Running loader`
    The loader does not belong to sections and always at the start of the pipeline. Note that the loader
-   loads the data using the specific :code:`pattern=projection` (See more :ref:`info_reslice`). The same pattern is used by the 
-   following section. 
+   loads the data using the specific :code:`pattern=projection` (See more :ref:`info_reslice`). The same pattern is used by the
+   following section.
 
 * :code:`Section N with the following methods`
-   Each section contains a number of methods that run sequentially for each :ref:`blocks_data` 
-   of data. When all blocks are processed, the user will see the message :code:`Finished processing the last block`. This means that all of the 
+   Each section contains a number of methods that run sequentially for each :ref:`blocks_data`
+   of data. When all blocks are processed, the user will see the message :code:`Finished processing the last block`. This means that all of the
    input data have been processed in this section and the pipeline moves to the next section, if it exists.
 
 * :code:`50%|#####     | 1/2 [00:02<00:02,  2.52s/block]`
-   These are the progress bars showing how much data is being processed in every section. 
-   The percentage progress bar demonstrates how many blocks have been processed by the `M` number of methods of the current section. Specifically in this case 
-   we have :code:`1/2`, which means that one of two blocks completed (hence `50%`). Then :code:`00:02<00:02` shows the time in seconds to 
-   reach the current block (time elapsed) and the remaining time to complete all iterations over blocks. The :code:`2.52s/block` part is an 
+   These are the progress bars showing how much data is being processed in every section.
+   The percentage progress bar demonstrates how many blocks have been processed by the `M` number of methods of the current section. Specifically in this case
+   we have :code:`1/2`, which means that one of two blocks completed (hence `50%`). Then :code:`00:02<00:02` shows the time in seconds to
+   reach the current block (time elapsed) and the remaining time to complete all iterations over blocks. The :code:`2.52s/block` part is an
    estimation of how much time it's taking per block. When the time per block is less than one second then this can be presented as :code:`block/s` instead.
-   See :code:`save_to_images` progress report, for instance. 
+   See :code:`save_to_images` progress report, for instance.
 
 .. note:: When interpreting progress bars, one possible misunderstanding can be an association of the progress with the methods completed. Because each piece of data (a block) can be processed by multiple methods, we report on how many blocks have been processed instead.
\ No newline at end of file
diff --git a/docs/source/howto/process_lists/httomo_parameters.rst b/docs/source/howto/process_lists/httomo_parameters.rst
index 334e4ecd8..ff5ed5fc1 100644
--- a/docs/source/howto/process_lists/httomo_parameters.rst
+++ b/docs/source/howto/process_lists/httomo_parameters.rst
@@ -19,6 +19,6 @@ used.
 
 .. toctree::
    :maxdepth: 1
-   
+
    side_outputs/side_out
    save_results/save_results
diff --git a/docs/source/howto/process_lists/process_list_configure.rst b/docs/source/howto/process_lists/process_list_configure.rst
index 4ec8a7ed9..3d93974f0 100644
--- a/docs/source/howto/process_lists/process_list_configure.rst
+++ b/docs/source/howto/process_lists/process_list_configure.rst
@@ -47,19 +47,19 @@ The pattern of any supported method can be found in :ref:`pl_library`.
 Library files
 -------------
 
-Here is the list of :ref:`pl_library` for backends where patterns and other fixed arguments for methods are specified. When HTTomo operates 
-with a certain method it always refers to its library file in order get the specific requirements for that method. 
+Here is the list of :ref:`pl_library` for backends where patterns and other fixed arguments for methods are specified. When HTTomo operates
+with a certain method it always refers to its library file in order get the specific requirements for that method.
 
 .. dropdown:: TomoPy's library file
 
-    .. literalinclude:: ../../../../httomo/methods_database/packages/external/tomopy/tomopy.yaml    
+    .. literalinclude:: ../../../../httomo/methods_database/packages/external/tomopy/tomopy.yaml
 
 .. dropdown:: Httomolibgpu's library file
-    
+
     .. literalinclude:: ../../../../httomo/methods_database/packages/external/httomolibgpu/httomolibgpu.yaml
 
 .. dropdown:: Httomolib's library file
-    
+
     .. literalinclude:: ../../../../httomo/methods_database/packages/external/httomolib/httomolib.yaml
 
 .. _pl_grouping:
diff --git a/docs/source/howto/process_lists/save_results/save_results.rst b/docs/source/howto/process_lists/save_results/save_results.rst
index 173de28a9..7291f782e 100644
--- a/docs/source/howto/process_lists/save_results/save_results.rst
+++ b/docs/source/howto/process_lists/save_results/save_results.rst
@@ -15,7 +15,7 @@ with the :code:`save_result` parameter. Its value is a boolean, so either
 Example 1: save output of a specific method
 ###########################################
 
-Suppose we wanted to save the output of the normalisation function :code:`normalize`. Then we 
+Suppose we wanted to save the output of the normalisation function :code:`normalize`. Then we
 should add :code:`save_result: True` to the list of the function parameters, but NOT the method's parameters:
 
 .. code-block:: yaml
@@ -39,7 +39,7 @@ output.
 
 In contrast to the previous example, suppose we had a process list where we
 would like to save the output of all methods using :code:`--save_all`, *apart* from the
-:code:`normalize` method. 
+:code:`normalize` method.
 
 .. code-block:: yaml
   :emphasize-lines: 8
diff --git a/docs/source/howto/process_lists_guide.rst b/docs/source/howto/process_lists_guide.rst
index 8f6800858..4888ac236 100644
--- a/docs/source/howto/process_lists_guide.rst
+++ b/docs/source/howto/process_lists_guide.rst
@@ -10,7 +10,7 @@ they are introduced here too.
 Editing process lists
 ---------------------
 
-This section explains how to build a process list (see more on :ref:`explanation_process_list`) from YAML templates 
+This section explains how to build a process list (see more on :ref:`explanation_process_list`) from YAML templates
 (see more on :ref:`explanation_templates`).
 
 Given time working with HTTomo, a user will likely settle on a workflow for
@@ -22,10 +22,10 @@ As a starting point, the general process of building the pipeline can be the fol
 
 - copy+paste templates for the desired methods from the
   :ref:`reference_templates` section
-- manually edit the parameter values within the copied template as needed. The user might want 
+- manually edit the parameter values within the copied template as needed. The user might want
   to check the documentation for the relevant method in the library itself.
 - intermittently run the :ref:`YAML checker <utilities_yamlchecker>` during
-  editing of the YAML file to detect any errors early on. It is strongly recommended to run 
+  editing of the YAML file to detect any errors early on. It is strongly recommended to run
   the checker at least once when the YAML pipeline is configured and ready to be run.
 
 Methods order
@@ -43,6 +43,6 @@ following:
 
 .. toctree::
    :maxdepth: 2
-   
+
    process_lists/httomo_parameters
    process_lists/process_list_configure
diff --git a/docs/source/howto/run_httomo.rst b/docs/source/howto/run_httomo.rst
index fd4f9e030..4a1ff5555 100644
--- a/docs/source/howto/run_httomo.rst
+++ b/docs/source/howto/run_httomo.rst
@@ -17,7 +17,7 @@ Required inputs
 
 In order to run HTTomo you require a data file (an HDF5 file) and a YAML process
 list file that describes the desired processing pipeline. For information on
-getting started creating this YAML file, please see :ref:`howto_process_list` 
+getting started creating this YAML file, please see :ref:`howto_process_list`
 and also ready-to-be-used :ref:`tutorials_pl_templates`.
 
 Running HTTomo Inside or Outside of Diamond
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 2ff2a2b06..316299d51 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -7,7 +7,7 @@
     :maxdepth: 2
     :glob:
 
-    introduction/about    
+    introduction/about
     explanation/templates
     explanation/process_list
     explanation/faq
@@ -50,7 +50,7 @@
     :glob:
 
     pipelines/yaml
-    
+
 .. _utilities_content:
 
 .. toctree::
@@ -66,5 +66,5 @@
     :caption: Developers
     :maxdepth: 2
 
-    developers/how_to_contribute    
+    developers/how_to_contribute
     developers/memory_calculation
diff --git a/docs/source/introduction/about.rst b/docs/source/introduction/about.rst
index 407e4a709..f72913776 100644
--- a/docs/source/introduction/about.rst
+++ b/docs/source/introduction/about.rst
@@ -19,7 +19,7 @@ HTTomo orchestrates the optimal data splitting driven by the available GPU memor
 
 HTTomo is a User Interface (UI) package and does not contain any data processing methods, but rather utilises other libraries as `backends <https://en.wikipedia.org/wiki/Frontend_and_backend>`_.
 Please see the list of currently supported packages by HTTomo in :ref:`backends_list`. It should be relatively simple to integrate any other modular
-CPU/GPU Python library for data processing methods in HTTomo, please see more on that in :ref:`developers_content` section. 
+CPU/GPU Python library for data processing methods in HTTomo, please see more on that in :ref:`developers_content` section.
 
 A complex data analysis pipelines can be built by stacking together provided :ref:`reference_templates` and :ref:`tutorials_pl_templates` are also provided.
 
diff --git a/docs/source/introduction/indepth/detailed_about.rst b/docs/source/introduction/indepth/detailed_about.rst
index a113a0f5f..cf5949c71 100644
--- a/docs/source/introduction/indepth/detailed_about.rst
+++ b/docs/source/introduction/indepth/detailed_about.rst
@@ -9,7 +9,7 @@ Here we present more detailed concepts of HTTomo's framework, such as,
 
 .. toctree::
    :maxdepth: 2
-   
+
    sections
    reslice
    wrappers
@@ -37,6 +37,6 @@ of data splitting produces a piece of data called a *block*.
 
 .. toctree::
    :maxdepth: 2
-   
+
    chunks
    blocks
\ No newline at end of file
diff --git a/docs/source/introduction/indepth/memory_estimators.rst b/docs/source/introduction/indepth/memory_estimators.rst
index 0720e96e0..2560a6992 100644
--- a/docs/source/introduction/indepth/memory_estimators.rst
+++ b/docs/source/introduction/indepth/memory_estimators.rst
@@ -5,9 +5,9 @@ Memory Estimators
 
 Memory estimators is the integral part of HTTomo when it comes to the GPU processing and efficient device-to-device transfers.
 
-.. note:: One needs to know how much of the data safely fits a GPU card. This is the main purpose of the GPU memory estimators in HTTomo. 
+.. note:: One needs to know how much of the data safely fits a GPU card. This is the main purpose of the GPU memory estimators in HTTomo.
 
-Based on the free memory available in the GPU device and memory requirements of a particular method, GPU memory estimators will initiate 
+Based on the free memory available in the GPU device and memory requirements of a particular method, GPU memory estimators will initiate
 :ref:`developers_memorycalc` to decide how much of the data can be safely transferred to the GPU for processing. The size of the data
 that can be fitted in the GPU card for computations will define the size of the :ref:`blocks_data`.
 
@@ -19,7 +19,7 @@ that can be fitted in the GPU card for computations will define the size of the
     Free memory of the GPU device and the method's memory requirement is the information that is required for memory estimators to define the size of the :ref:`blocks_data` in the :ref:`info_sections`.
 
 As one would like to minimise device-to-device transfers while doing GPU computations, HTTomo tries to chain the methods together in one Section.
-Therefore if there are multiple methods chained together, the size of the block will be defined by the most memory-demanding method. 
+Therefore if there are multiple methods chained together, the size of the block will be defined by the most memory-demanding method.
 Memory estimators will inspect every method in that methods' chain in order to decide which one dominates.
 
 .. _fig_memest2:
diff --git a/docs/source/introduction/indepth/reslice.rst b/docs/source/introduction/indepth/reslice.rst
index 4fd649cdf..6147093aa 100644
--- a/docs/source/introduction/indepth/reslice.rst
+++ b/docs/source/introduction/indepth/reslice.rst
@@ -2,9 +2,9 @@
 
 Re-slicing
 ----------
-The re-slicing of data happens when we need to access a slice which is orthogonal to the current one. 
-In tomography, we normally work in the space of projections or in the space of sinograms. Different methods require different slicing 
-orientations, or, as we call it, a *pattern*. The change of the pattern is a **re-slice** operation or a transformation of an array by 
+The re-slicing of data happens when we need to access a slice which is orthogonal to the current one.
+In tomography, we normally work in the space of projections or in the space of sinograms. Different methods require different slicing
+orientations, or, as we call it, a *pattern*. The change of the pattern is a **re-slice** operation or a transformation of an array by
 re-slicing in a particular direction. For instance, from the projection space/pattern to the sinogram space/patterns, as in the figure below.
 
 .. _fig_reslice:
@@ -14,8 +14,8 @@ re-slicing in a particular direction. For instance, from the projection space/pa
 
     The re-slicing operation for tomographic data. Here the data is resliced from the stack of projections to the stack of sinograms.
 
-In HTTomo, the re-slicing operation is performed on the CPU as we need to access all the data. Even if the pipeline consists of only GPU methods stacked together, 
-the re-slicing step will transfer the data from the GPU device to the CPU memory first. This operation can be costly for big datasets and we recommend to minimise the number of 
+In HTTomo, the re-slicing operation is performed on the CPU as we need to access all the data. Even if the pipeline consists of only GPU methods stacked together,
+the re-slicing step will transfer the data from the GPU device to the CPU memory first. This operation can be costly for big datasets and we recommend to minimise the number of
 re-slicing operations in your pipeline. Normally for tomographic pre-processing and reconstruction there is just one re-slice needed, please see how :ref:`howto_process_list`.
 
 .. _fig_reslice2:
diff --git a/docs/source/introduction/indepth/sections.rst b/docs/source/introduction/indepth/sections.rst
index 9fb2f88ba..3da7e5835 100644
--- a/docs/source/introduction/indepth/sections.rst
+++ b/docs/source/introduction/indepth/sections.rst
@@ -5,7 +5,7 @@ Sections
 
 Sections is the fundamental concept of the HTTomo's framework which is related to how the I/O operations and processing of data is organised.
 
-.. note:: The main purpose of a section is to organise the data input/output workflow, as well as, chain together the methods so that the constructed pipeline is computationally efficient. 
+.. note:: The main purpose of a section is to organise the data input/output workflow, as well as, chain together the methods so that the constructed pipeline is computationally efficient.
 
 To better understand the purpose of the section it is also useful to read information about :ref:`chunks_data`, :ref:`blocks_data` and :ref:`info_memory_estimators`.
 
@@ -33,7 +33,7 @@ Example 1: Sections with re-slice
     :alt: Sections in pipelines
 
     Let us say that the pattern in methods `M`\ :sub:`1-3` is *projection* and methods in `M`\ :sub:`4-5` belong to *sinogram* pattern.
-    This will result in two sections created and also :ref:`info_reslice` operation in the data transfer `T`\ :sub:`3` layer. 
+    This will result in two sections created and also :ref:`info_reslice` operation in the data transfer `T`\ :sub:`3` layer.
 
 Example 2 : Sections with re-slice and data saving
 ==================================================
@@ -43,10 +43,10 @@ Example 2 : Sections with re-slice and data saving
     :scale: 50 %
     :alt: Sections in pipelines
 
-    In addition Example 1 situation, let us assume that we want to save the result of `M`\ :sub:`2` method to the disk. 
+    In addition Example 1 situation, let us assume that we want to save the result of `M`\ :sub:`2` method to the disk.
     This means that even though `M`\ :sub:`1-3` methods can be performed on the GPU, the data will be transferred to CPU.
-    The pipeline will be further fragmented to introduce another section, so that the data transfer `T`\ :sub:`2` layer also saves the data on the 
-    disk, as well as, taking care to return the data back on the GPU for the method `M`\ :sub:`3`. 
+    The pipeline will be further fragmented to introduce another section, so that the data transfer `T`\ :sub:`2` layer also saves the data on the
+    disk, as well as, taking care to return the data back on the GPU for the method `M`\ :sub:`3`.
 
 Example 3 : Sections with side outputs
 ======================================
@@ -59,6 +59,6 @@ Example 3 : Sections with side outputs
     Consider, again, Example 1. Here, however, `M`\ :sub:`5` requests the :ref:`side_output` of the method `M`\ :sub:`4`.
     This, for example, can be because the reconstruction method `M`\ :sub:`5` requires the :ref:`centering` value of `M`\ :sub:`4`, where
     this value is calculated. This divides `M`\ :sub:`4` and `M`\ :sub:`5` into separate sections. Also notice that `M`\ :sub:`1` needs the data
-    to be saved on disk, so in total, it is a pipeline with 4 sections in it. 
+    to be saved on disk, so in total, it is a pipeline with 4 sections in it.
 
 .. note:: It can be seen that creating more sections in pipelines is to be avoided when building an efficient pipeline. Creating a section usually leads to synchronisation of all processes on the CPU and potentially, if not enough memory, through-disk operations.
diff --git a/docs/source/introduction/indepth/wrappers.rst b/docs/source/introduction/indepth/wrappers.rst
index 8c267d9e9..6c07cc529 100644
--- a/docs/source/introduction/indepth/wrappers.rst
+++ b/docs/source/introduction/indepth/wrappers.rst
@@ -3,16 +3,16 @@
 Method Wrappers
 ===============
 
-HTTomo does not contain image processing methods and uses external libraries for processing, see :ref:`backends_list`. 
-However, because methods process data in a different manner, there's a need to tell the framework what kind of processing that is. 
+HTTomo does not contain image processing methods and uses external libraries for processing, see :ref:`backends_list`.
+However, because methods process data in a different manner, there's a need to tell the framework what kind of processing that is.
 
-HTTomo currently has the following wrappers: 
+HTTomo currently has the following wrappers:
 
 1. The generic wrapper. Suitable when the method does not change the dimensions of the data for its output and requires one input and one output. This also can be called a filter.
 
 2. Normalisation wrapper is to deal with normalisation methods as they have supplementary data as an input (e.g. flats/darks) and one dataset as an output.
 
-3. Rotation or Centering wrapper. These are special methods to estimate :ref:`centering` automatically. The input can be quite specific, e.g. normalised sinogram or selected projections, and the output is usually a scalar. 
+3. Rotation or Centering wrapper. These are special methods to estimate :ref:`centering` automatically. The input can be quite specific, e.g. normalised sinogram or selected projections, and the output is usually a scalar.
 
 4. Reconstruction wrapper. Reconstruction needs an additional information to be passed like angles and the centre of rotation. This is all handled by the framework in the wrapper.
 
diff --git a/docs/source/pipelines/yaml.rst b/docs/source/pipelines/yaml.rst
index e1087a4c7..a45c2572b 100644
--- a/docs/source/pipelines/yaml.rst
+++ b/docs/source/pipelines/yaml.rst
@@ -3,7 +3,7 @@
 Full YAML pipelines
 ==============================
 
-This is a collection of ready to be used pipeline templates aka process lists. 
+This is a collection of ready to be used pipeline templates aka process lists.
 See more on :ref:`explanation_process_list` and how to :ref:`howto_process_list`.
 
 .. _tutorials_pl_templates_cpu:
@@ -16,11 +16,11 @@ CPU Pipeline templates
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/pipeline_cpu1.yaml
 
 .. dropdown:: TomoPy's pipeline where :ref:`previewing` is demonstrated
-    
+
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/pipeline_cpu2.yaml
 
 .. dropdown:: This pipeline shows how "calculate_stats" module extracts global statistics in order to be passed to "save_to_images" function which uses it to rescale data for saving images
-    
+
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/pipeline_cpu2.yaml
 
 .. _tutorials_pl_templates_gpu:
@@ -29,7 +29,7 @@ GPU Pipeline templates
 ----------------------------
 
 .. dropdown:: Basic GPU pipeline which uses functions from the httomolibgpu library.
-    
+
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/pipeline_gpu1.yaml
 
 
@@ -42,12 +42,12 @@ DLS Specific templates
 
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/DLS/01_diad_pipeline_gpu.yaml
 
-.. dropdown:: GPU-driven pipeline for the 360-degrees data which estimates the CoR value and the overlap. The 180-degrees sinogram is obtained by stitching using the overlap value. The pipeline shows the extensive use of side_outputs and refrencing. 
-    
+.. dropdown:: GPU-driven pipeline for the 360-degrees data which estimates the CoR value and the overlap. The 180-degrees sinogram is obtained by stitching using the overlap value. The pipeline shows the extensive use of side_outputs and refrencing.
+
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/pipeline_360deg_gpu2.yaml
 
-.. dropdown:: More advanced GPU pipeline for the 360-degrees data. Here we preview the section and then reconstruct it iteratively, the result then downsampled before saving smaller images. 
-    
+.. dropdown:: More advanced GPU pipeline for the 360-degrees data. Here we preview the section and then reconstruct it iteratively, the result then downsampled before saving smaller images.
+
     .. literalinclude:: ../../../tests/samples/pipeline_template_examples/pipeline_360deg_iterative_gpu3.yaml
 
 .. _tutorials_pl_templates_sweeps:
diff --git a/docs/source/reference/yaml.rst b/docs/source/reference/yaml.rst
index a22792953..0d60fcbb4 100644
--- a/docs/source/reference/yaml.rst
+++ b/docs/source/reference/yaml.rst
@@ -68,7 +68,7 @@ to use. This can be especially helpful for those who are less familiar with prog
 or are new to the specific tools and libraries you are using.
 
 We have a :ref:`utilities_yamlchecker` that can help you to validate your YAML file.
-Before running your pipeline, we highly recommend that you validate your YAML file using this utility. 
+Before running your pipeline, we highly recommend that you validate your YAML file using this utility.
 The checker will help you to identify errors in your YAML file.
 
 We also recommend to use editors that `support <https://ozone.one/top-5-open-source-yaml-editors-for-streamlined-coding/>`_ YAML format naturally, e.g.: Atom, Visual Studio Code, Notepad++, and others.
diff --git a/docs/source/utilities/yaml_checker.rst b/docs/source/utilities/yaml_checker.rst
index e1afdc073..eee5eaf31 100644
--- a/docs/source/utilities/yaml_checker.rst
+++ b/docs/source/utilities/yaml_checker.rst
@@ -2,7 +2,7 @@
 
 YAML Checker - Why use it?
 **************************
-YAML checker will help you to validate your process list (see :ref:`explanation_process_list`) 
+YAML checker will help you to validate your process list (see :ref:`explanation_process_list`)
 saved as a YAML file. Before running your pipeline with HTTomo, we highly recommend that you validate your process list using this utility. **The checker will help you to identify errors in your process list and avoid problems during the run**.
 
 Usage
diff --git a/docs/source/utilities/yaml_generator.rst b/docs/source/utilities/yaml_generator.rst
index 7e5e98e48..63345b2af 100644
--- a/docs/source/utilities/yaml_generator.rst
+++ b/docs/source/utilities/yaml_generator.rst
@@ -2,13 +2,13 @@
 
 Templates generator
 *******************
-:ref:`backends_content` can be generated automatically using the YAML generator tool `provided <https://github.com/DiamondLightSource/httomo/tree/main/scripts/yaml_templates_generator.py>`_ in the Github repo. 
+:ref:`backends_content` can be generated automatically using the YAML generator tool `provided <https://github.com/DiamondLightSource/httomo/tree/main/scripts/yaml_templates_generator.py>`_ in the Github repo.
 
-The script does the following: 
+The script does the following:
 
 * Generates a list of YAML files for all accessible on import methods in a chosen software package, e.g., TomoPy.
 * Modifies and/or removes some extracted parameters in YAML templates to make the templates compatible with HTTomo.
- 
+
 How does it work:
 
 * The user would need to provide a YAML file with the listed *modules* you would like to inspect and extract the methods from. For instance, for the TomoPy package this would be:
@@ -19,15 +19,15 @@ How does it work:
     - tomopy.misc.morph
 
 * The generator can be applied using the following command:
- 
+
 .. code-block:: console
-   
+
    $ python -m yaml_templates_generator -i /path/to/modules.yaml -o /path/to/outputfolder/
 
 Please note that the package from which the modules are extracted, must be installed into your conda environment.
 
 **For TomoPy templates only.** After templates have been generated for TomoPy, we need to remove the ones that are not currently supported by HTTomo. We do that by looking into the library file that exists in HTTomo for TomoPy.
- 
+
 .. code-block:: console
-   
+
    $ python -m remove_unsupported_templates -t /path/to/templates/ -l /path/to/library/file
diff --git a/httomo/block_interfaces.py b/httomo/block_interfaces.py
index 2864dc541..fd752d3bd 100644
--- a/httomo/block_interfaces.py
+++ b/httomo/block_interfaces.py
@@ -15,15 +15,19 @@ class BlockTransfer(Protocol):
     `MethodWrapper`
     """
 
-    def to_gpu(self): ...  # pragma: no cover
+    def to_gpu(self):
+        ...  # pragma: no cover
 
-    def to_cpu(self): ...  # pragma: no cover
+    def to_cpu(self):
+        ...  # pragma: no cover
 
     @property
-    def is_gpu(self) -> bool: ...  # pragma: no cover
+    def is_gpu(self) -> bool:
+        ...  # pragma: no cover
 
     @property
-    def is_cpu(self) -> bool: ...  # pragma: no cover
+    def is_cpu(self) -> bool:
+        ...  # pragma: no cover
 
 
 class BlockData(Protocol):
@@ -40,57 +44,72 @@ def __dir__(self) -> list[str]:
         ...  # pragma: no cover
 
     @property
-    def data(self) -> generic_array: ...  # pragma: no cover
+    def data(self) -> generic_array:
+        ...  # pragma: no cover
 
     @data.setter
-    def data(self, new_data: generic_array): ...  # pragma: no cover
-    
+    def data(self, new_data: generic_array):
+        ...  # pragma: no cover
+
     @property
-    def aux_data(self) -> AuxiliaryData: ...  # pragma: no cover
+    def aux_data(self) -> AuxiliaryData:
+        ...  # pragma: no cover
 
     @property
-    def angles(self) -> np.ndarray: ...  # pragma: no cover
+    def angles(self) -> np.ndarray:
+        ...  # pragma: no cover
 
     @angles.setter
-    def angles(self, new_angles: np.ndarray): ...  # pragma: no cover
+    def angles(self, new_angles: np.ndarray):
+        ...  # pragma: no cover
 
     @property
-    def angles_radians(self) -> np.ndarray: ...  # pragma: no cover
+    def angles_radians(self) -> np.ndarray:
+        ...  # pragma: no cover
 
     @angles_radians.setter
-    def angles_radians(self, new_angles: np.ndarray): ...  # pragma: no cover
+    def angles_radians(self, new_angles: np.ndarray):
+        ...  # pragma: no cover
 
     @property
-    def darks(self) -> generic_array: ...  # pragma: no cover
+    def darks(self) -> generic_array:
+        ...  # pragma: no cover
 
     @darks.setter
-    def darks(self, darks: generic_array): ...  # pragma: no cover
+    def darks(self, darks: generic_array):
+        ...  # pragma: no cover
 
     # alias
     @property
-    def dark(self) -> generic_array: ...  # pragma: no cover
+    def dark(self) -> generic_array:
+        ...  # pragma: no cover
 
     @dark.setter
-    def dark(self, darks: generic_array): ...  # pragma: no cover
+    def dark(self, darks: generic_array):
+        ...  # pragma: no cover
 
     @property
-    def flats(self) -> generic_array: ...  # pragma: no cover
+    def flats(self) -> generic_array:
+        ...  # pragma: no cover
 
     @flats.setter
-    def flats(self, flats: generic_array): ...  # pragma: no cover
+    def flats(self, flats: generic_array):
+        ...  # pragma: no cover
 
     # alias
     @property
-    def flat(self) -> generic_array: ...  # pragma: no cover
+    def flat(self) -> generic_array:
+        ...  # pragma: no cover
 
     @flat.setter
-    def flat(self, flats: generic_array): ...  # pragma: no cover
+    def flat(self, flats: generic_array):
+        ...  # pragma: no cover
 
     @property
     def shape(self) -> Tuple[int, int, int]:
         """Shape of the data in this block"""
         ...  # pragma: no cover
-        
+
     @property
     def shape_unpadded(self) -> Tuple[int, int, int]:
         """Shape of the core date in this block, with padding removed"""
@@ -107,7 +126,7 @@ class BlockIndexing(Protocol):
     def chunk_index(self) -> Tuple[int, int, int]:
         """The index of this block within the chunk handled by the current process"""
         ...  # pragma: no cover
-        
+
     @property
     def chunk_index_unpadded(self) -> Tuple[int, int, int]:
         """The index of the core area of this block within the chunk (padding removed)"""
@@ -117,7 +136,7 @@ def chunk_index_unpadded(self) -> Tuple[int, int, int]:
     def chunk_shape(self) -> Tuple[int, int, int]:
         """Shape of the full chunk handled by the current process"""
         ...  # pragma: no cover
-        
+
     @property
     def chunk_shape_unpadded(self) -> Tuple[int, int, int]:
         """Shape of the full chunk core area handled by the current process (with padding removed)"""
@@ -127,7 +146,7 @@ def chunk_shape_unpadded(self) -> Tuple[int, int, int]:
     def global_index(self) -> Tuple[int, int, int]:
         """The index of this block within the global data across all processes"""
         ...  # pragma: no cover
-        
+
     @property
     def global_index_unpadded(self) -> Tuple[int, int, int]:
         """The index of the core area of this block within the global data across all processes (with padding removed)"""
@@ -145,28 +164,28 @@ def is_last_in_chunk(self) -> bool:
         process
         """
         ...  # pragma: no cover
-        
+
     @property
     def slicing_dim(self) -> Literal[0, 1, 2]:
         """Return the slicing dimenions of the block"""
         ...  # pragma: no cover
-        
+
     @property
     def is_padded(self) -> bool:
         """Determine if this is a padded block"""
         ...  # pragma: no cover
-        
+
     @property
     def padding(self) -> Tuple[int, int]:
-        """Get the 'before' and 'after' padding values for this block. 
+        """Get the 'before' and 'after' padding values for this block.
         This is to be understood as the the number of padding slices in the 'slicing_dim' direction
-        that come before and after the core area of the block. 
+        that come before and after the core area of the block.
         If no padding is used, it returns (0, 0).
         """
         ...  # pragma: no cover
 
     @property
-    def data_unpadded(self) -> generic_array: 
+    def data_unpadded(self) -> generic_array:
         """Return the data, but with the padding slices removed"""
         ...  # pragma: no cover
 
diff --git a/httomo/cli.py b/httomo/cli.py
index bd6b2eeb4..44711d97f 100644
--- a/httomo/cli.py
+++ b/httomo/cli.py
@@ -40,7 +40,8 @@ def main():
 @click.argument(
     "in_data_file",
     type=click.Path(exists=True, dir_okay=False, path_type=Path),
-    required=False, default=None,
+    required=False,
+    default=None,
 )
 def check(yaml_config: Path, in_data_file: Optional[Path] = None):
     """Check a YAML pipeline file for errors."""
@@ -86,27 +87,29 @@ def check(yaml_config: Path, in_data_file: Optional[Path] = None):
     "--max-cpu-slices",
     type=click.INT,
     default=64,
-    help="Maximum number of slices to use for a block for CPU-only sections (default: 64)"
+    help="Maximum number of slices to use for a block for CPU-only sections (default: 64)",
 )
 @click.option(
     "--max-memory",
     type=click.STRING,
     default="0",
-    help="Limit the amount of memory used by the pipeline to the given memory (supports strings like 3.2G or bytes)"
+    help="Limit the amount of memory used by the pipeline to the given memory (supports strings like 3.2G or bytes)",
 )
 @click.option(
     "--monitor",
     type=click.STRING,
     multiple=True,
     default=[],
-    help=("Add monitor to the runner (can be given multiple times). " +
-          f"Available monitors: {', '.join(MONITORS_MAP.keys())}")
+    help=(
+        "Add monitor to the runner (can be given multiple times). "
+        + f"Available monitors: {', '.join(MONITORS_MAP.keys())}"
+    ),
 )
 @click.option(
     "--monitor-output",
-    type=click.File('w'),
+    type=click.File("w"),
     default=sys.stdout,
-    help="File to store the monitoring output. Defaults to '-', which denotes stdout"
+    help="File to store the monitoring output. Defaults to '-', which denotes stdout",
 )
 @click.option(
     "--syslog-host",
diff --git a/httomo/data/dataset_store.py b/httomo/data/dataset_store.py
index f4614d218..ac72d0b7b 100644
--- a/httomo/data/dataset_store.py
+++ b/httomo/data/dataset_store.py
@@ -1,3 +1,24 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# ---------------------------------------------------------------------------
+# Copyright 2023 Diamond Light Source Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ecpress or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+# Created By  : Tomography Team at DLS <scientificsoftware@diamond.ac.uk>
+
+# ---------------------------------------------------------------------------
+
 import logging
 from os import PathLike
 from pathlib import Path
@@ -19,77 +40,6 @@
 
 from httomo.utils import log_once, make_3d_shape_from_shape
 
-"""
-This is from the final handover call:
-
-# Cases
-
-- *We always process the data in blocks*
-
-## 1 Process, all fits memory
-
-- chunk_shape = global_shape   
-- chunk_index = (0, 0, 0)
-- the whole chunk is a numpy array, in a `DataSet` in-memory
-- `write_block` -> writes a `DataSetBlock` into the chunk `DataSet`
-- `read_block` -> get a block, which might be the full size of the `DataSet` for the chunk, 
-  it's an in-memory slice of the chunk `DataSet`
-
-### Reslice
-
-- does nothing mostly
-- updates `slicing_dim` somewhere, so that `read_block` knows how to slice the block
-
-## 2 Processes, all fits in memory (or more)
-
-(assume slicing dim is 0)
-
-- chunk_shape < global_shape, e.g. `(5, 20, 30) < (10, 20, 30)`
-- `chunk_index`: in rank 0: `(0, 0, 0)`, in rank 1: `(5, 0, 0)`
-- the whole chunk is in numpy array, in a `DataSet`, in-memory
-- BUT: each process as a part of the global data in-memory
-- `write_block` -> writes a `DataSetBlock` into the chunk `DataSet`
-- `read_block` -> get a block, which might be the full size of the `DataSet` for the chunk
-
-### Reslice
-
-- call the MPI memory-based reslice function we always had (using `MPI.allgather`)
-- we have a new chunk in each process, wich chunk_shape=`(10, 10, 30)`
-- we have `chunk_index`: in rank 0: `(0, 0, 0)`, in rank 1: `(0, 10, 0)`
-- updates `slicing_dim` somewhere, so that `read_block` knows how to slice the block
-
-
-## 2 Processes, doesn't memory (or more)
-
-(assume slicing dim is 0)
-
-- chunk_shape < global_shape, e.g. `(5, 20, 30) < (10, 20, 30)`
-- `chunk_index`: in rank 0: `(0, 0, 0)`, in rank 1: `(5, 0, 0)`
-- *the global data is fully is a single h5py file*
-  - it is the whole global cube!
-  - each process only needs to access a chunk, block-wise, out of that file
-  - has the same interface as numpy array (`shape`, indexing, ...)
-  - BUT: as soon as we index, we read the file into an in-memory numpy array
-  - --> we cannot create a "view" of a subset of the file, referencing the disk
-  - so, each process needs to keep track of the start of its chunk within the file, 
-    and when read_block is called with start index 0, we add the chunk_index to that
-- `write_block` -> writes a `DataSetBlock` into the file, with the correct offset (`chunk_index + block_index`)
-- `read_block` -> get a block from the file, with offset `chunk_index + block_index`
-- --> `FullFileDataSet` class takes care of that
-
-### Reslice
-
-- we have full globally shared file, all the data is there on disk already
-- does nothing mostly
-- updates `slicing_dim` somewhere, so that `read_block` knows how to slice the block
-- update `chunk_index`: in rank 0: `(0, 0, 0)`, in rank 1: `(0, 10, 0)`
-
-
-"""
-
-# Notes:
-# - refactoring the nested if into separate function is badly needed
-
 
 class DataSetStoreWriter(ReadableDataSetSink):
     """A DataSetSink that can be used to store block-wise data in the current chunk (for the current process).
@@ -541,9 +491,9 @@ def _mpi_exchange_padding_area_before(self):
             pad_slices = [slice(None), slice(None), slice(None)]
             pad_slices[self._slicing_dim] = slice(0, self._padding[0])
 
-            self._data[pad_slices[0], pad_slices[1], pad_slices[2]] = (
-                receive_buf_from_left_neighbour
-            )
+            self._data[
+                pad_slices[0], pad_slices[1], pad_slices[2]
+            ] = receive_buf_from_left_neighbour
 
     def _mpi_exchange_padding_area_after(self):
         MPI_TAG = 44
@@ -583,9 +533,9 @@ def _mpi_exchange_padding_area_after(self):
                 self._chunk_shape[self._slicing_dim] - self._padding[1],
                 self._chunk_shape[self._slicing_dim],
             )
-            self._data[pad_slices[0], pad_slices[1], pad_slices[2]] = (
-                receive_buf_from_right_neighbour
-            )
+            self._data[
+                pad_slices[0], pad_slices[1], pad_slices[2]
+            ] = receive_buf_from_right_neighbour
 
     def _extend_data_for_padding(self, core_data: np.ndarray) -> np.ndarray:
         padded_data = np.empty(self._chunk_shape, self._data.dtype)
diff --git a/httomo/data/hdf/_utils/reslice.py b/httomo/data/hdf/_utils/reslice.py
index 161562a65..2ba6dc4b3 100644
--- a/httomo/data/hdf/_utils/reslice.py
+++ b/httomo/data/hdf/_utils/reslice.py
@@ -59,11 +59,7 @@ def reslice(
 
     # all-to-all MPI call distributes every processes list to every other process,
     # and we concatenate them again across the resliced dimension
-    new_data = numpy.concatenate(
-        alltoall(to_scatter, comm), axis=current_slice_dim - 1
-    )
+    new_data = numpy.concatenate(alltoall(to_scatter, comm), axis=current_slice_dim - 1)
 
-    start_idx = 0 if comm.rank == 0 else split_indices[comm.rank-1]
+    start_idx = 0 if comm.rank == 0 else split_indices[comm.rank - 1]
     return new_data, next_slice_dim, start_idx
-
-
diff --git a/httomo/data/mpiutil.py b/httomo/data/mpiutil.py
index ad4f6fa3a..0f572142f 100644
--- a/httomo/data/mpiutil.py
+++ b/httomo/data/mpiutil.py
@@ -41,9 +41,7 @@ def alltoall(arrays: List[np.ndarray], comm: MPI.Comm) -> List[np.ndarray]:
     """
 
     if len(arrays) != comm.size:
-        err_str = (
-            "list of arrays for MPI alltoall call must match communicator size"
-        )
+        err_str = "list of arrays for MPI alltoall call must match communicator size"
         raise ValueError(err_str)
 
     assert all(type(a) == np.ndarray for a in arrays), "All arrays must be numpy arrays"
@@ -105,9 +103,7 @@ def alltoall(arrays: List[np.ndarray], comm: MPI.Comm) -> List[np.ndarray]:
     sizes_rec1 = [s // factor for s in sizes_rec]
 
     # now send the same data, but with the adjusted size+datatype (output is identical)
-    comm.Alltoallv(
-        (fullinput, sizes_send1, dtype1), (fulloutput, sizes_rec1, dtype1)
-    )
+    comm.Alltoallv((fullinput, sizes_send1, dtype1), (fulloutput, sizes_rec1, dtype1))
 
     # build list of output arrays
     cumsizes = np.cumsum(sizes_rec)
diff --git a/httomo/data/param_sweep_store.py b/httomo/data/param_sweep_store.py
index 87ce04ca3..8f0e2f458 100644
--- a/httomo/data/param_sweep_store.py
+++ b/httomo/data/param_sweep_store.py
@@ -53,7 +53,7 @@ def read_sweep_results(self) -> ParamSweepBlock:
         return ParamSweepBlock(
             data=self._data[slices[0], slices[1], slices[2]],
             aux_data=self.aux_data,
-            slicing_dim=self.extract_dim
+            slicing_dim=self.extract_dim,
         )
 
 
@@ -80,9 +80,7 @@ def concat_dim(self) -> Literal[0, 1, 2]:
     @property
     def single_shape(self) -> Tuple[int, int, int]:
         if self._single_shape is None:
-            err_str = (
-                "Shape of single sweep result isn't known until the first write has occurred"
-            )
+            err_str = "Shape of single sweep result isn't known until the first write has occurred"
             raise ValueError(err_str)
         return self._single_shape
 
@@ -111,7 +109,9 @@ def increment_no_of_sweeps_written(self):
     @property
     def slices_per_sweep(self) -> int:
         if self._slices_per_sweep is None:
-            raise ValueError("Slices per sweep isn't known until the first write has occurred")
+            raise ValueError(
+                "Slices per sweep isn't known until the first write has occurred"
+            )
         return self._slices_per_sweep
 
     @property
diff --git a/httomo/globals.py b/httomo/globals.py
index f1adb6bac..e21934419 100644
--- a/httomo/globals.py
+++ b/httomo/globals.py
@@ -4,9 +4,7 @@
 run_out_dir: os.PathLike = Path(".")
 gpu_id: int = -1
 # maximum slices to use in CPU-only section
-MAX_CPU_SLICES: int = (
-    64  # A some random number which will be overwritten by --max-cpu_slices flag during runtime
-)
+MAX_CPU_SLICES: int = 64  # A some random number which will be overwritten by --max-cpu_slices flag during runtime
 SYSLOG_SERVER = "localhost"
 SYSLOG_PORT = 514
 FRAMES_PER_CHUNK: int = 1
diff --git a/httomo/logger.py b/httomo/logger.py
index 9a80a3651..fbec6ff73 100644
--- a/httomo/logger.py
+++ b/httomo/logger.py
@@ -14,7 +14,9 @@ def setup_logger(out_path: Path):
     # Concise logs displayed in terminal
     logger.add(sink=sys.stdout, level="INFO", colorize=True, format="{message}")
     # Concise logs written to file
-    logger.add(sink=concise_logfile_path, level="INFO", colorize=False, format="{message}")
+    logger.add(
+        sink=concise_logfile_path, level="INFO", colorize=False, format="{message}"
+    )
     # Verbose logs written to file
     logger.add(sink=verbose_logfile_path, level="DEBUG", colorize=False, enqueue=True)
     # Verbose logs sent to syslog server in GELF format
diff --git a/httomo/method_wrappers/generic.py b/httomo/method_wrappers/generic.py
index 8b27141df..f87f108c1 100644
--- a/httomo/method_wrappers/generic.py
+++ b/httomo/method_wrappers/generic.py
@@ -121,7 +121,7 @@ def __init__(
             raise ValueError("GPU is not available, please use only CPU methods")
 
         self._side_output: Dict[str, Any] = dict()
-        
+
         self._gpu_time_info = GpuTimeInfo()
 
         if gpu_enabled:
@@ -167,7 +167,7 @@ def is_cpu(self) -> bool:
     @property
     def is_gpu(self) -> bool:
         return not self.is_cpu
-    
+
     @property
     def gpu_time(self) -> GpuTimeInfo:
         return self._gpu_time_info
@@ -243,11 +243,15 @@ def _build_kwargs(
             elif p == "gpu_id":
                 assert gpu_enabled, "methods with gpu_id parameter require GPU support"
                 ret[p] = self._gpu_id
-            elif p == 'axis' and p in remaining_dict_params and remaining_dict_params[p] == 'auto':
+            elif (
+                p == "axis"
+                and p in remaining_dict_params
+                and remaining_dict_params[p] == "auto"
+            ):
                 ret[p] = self.pattern.value
                 pass
             elif p in remaining_dict_params:
-                ret[p] = remaining_dict_params[p]            
+                ret[p] = remaining_dict_params[p]
             elif p in self._params_with_defaults:
                 pass
             else:
@@ -291,7 +295,9 @@ def execute(self, block: T) -> T:
         block = self._transfer_data(block)
         with catch_gputime() as t:
             block = self._preprocess_data(block)
-            args = self._build_kwargs(self._transform_params(self._config_params), block)
+            args = self._build_kwargs(
+                self._transform_params(self._config_params), block
+            )
             block = self._run_method(block, args)
             block = self._postprocess_data(block)
 
diff --git a/httomo/method_wrappers/images.py b/httomo/method_wrappers/images.py
index 22c20ffca..3c88582db 100644
--- a/httomo/method_wrappers/images.py
+++ b/httomo/method_wrappers/images.py
@@ -44,8 +44,8 @@ def __init__(
         self["out_dir"] = out_dir if out_dir is not None else httomo.globals.run_out_dir
         if "comm_rank" in self.parameters:
             raise ValueError(
-                "save_to_images with the comm_rank parameter is broken. " +
-                "Please upgrade to the latest version, taking an offset parameter"
+                "save_to_images with the comm_rank parameter is broken. "
+                + "Please upgrade to the latest version, taking an offset parameter"
             )
 
     # Images execute is leaving original data on the device where it is,
@@ -58,7 +58,7 @@ def execute(self, block: T) -> T:
                 **self._config_params,
                 "offset": block.global_index[_get_slicing_dim(self.pattern) - 1],
             }
-            
+
         args = self._build_kwargs(self._transform_params(config_params), block)
         if block.is_gpu:
             with catchtime() as t:
diff --git a/httomo/method_wrappers/reconstruction.py b/httomo/method_wrappers/reconstruction.py
index 074b52db2..42679c31c 100644
--- a/httomo/method_wrappers/reconstruction.py
+++ b/httomo/method_wrappers/reconstruction.py
@@ -18,8 +18,10 @@ def should_select_this_class(cls, module_path: str, method_name: str) -> bool:
 
     def _preprocess_data(self, block: T) -> T:
         # this is essential for the angles cutting below to be valid
-        assert self.pattern == Pattern.sinogram, "reconstruction methods must be sinogram"
-        
+        assert (
+            self.pattern == Pattern.sinogram
+        ), "reconstruction methods must be sinogram"
+
         # for 360 degrees data the angular dimension will be truncated while angles are not.
         # Truncating angles if the angular dimension has got a different size
         datashape0 = block.data.shape[0]
diff --git a/httomo/method_wrappers/rotation.py b/httomo/method_wrappers/rotation.py
index 4430d14b7..4966b8a51 100644
--- a/httomo/method_wrappers/rotation.py
+++ b/httomo/method_wrappers/rotation.py
@@ -188,8 +188,16 @@ def _run_method(self, block: T, args: Dict[str, Any]) -> T:
     def normalize_sino(
         self, sino: np.ndarray, flats: Optional[np.ndarray], darks: Optional[np.ndarray]
     ) -> np.ndarray:
-        flats1d = 1.0 if (flats is None or flats.size == 0) else flats.mean(0, dtype=np.float32)
-        darks1d = 0.0 if (darks is None or darks.size == 0) else darks.mean(0, dtype=np.float32)
+        flats1d = (
+            1.0
+            if (flats is None or flats.size == 0)
+            else flats.mean(0, dtype=np.float32)
+        )
+        darks1d = (
+            0.0
+            if (darks is None or darks.size == 0)
+            else darks.mean(0, dtype=np.float32)
+        )
         denom = flats1d - darks1d
         sino = sino.astype(np.float32)
         if np.shape(denom) == tuple():
diff --git a/httomo/method_wrappers/save_intermediate.py b/httomo/method_wrappers/save_intermediate.py
index ef7b45486..ab8bce2fa 100644
--- a/httomo/method_wrappers/save_intermediate.py
+++ b/httomo/method_wrappers/save_intermediate.py
@@ -16,23 +16,32 @@
 
 
 class SaveIntermediateFilesWrapper(GenericMethodWrapper):
-    
     @classmethod
     def should_select_this_class(cls, module_path: str, method_name: str) -> bool:
         return method_name == "save_intermediate_data"
-    
-    def __init__(self, 
-                 method_repository: MethodRepository, 
-                 module_path: str, 
-                 method_name: str, 
-                 comm: Comm, 
-                 save_result: Optional[bool] = None,
-                 output_mapping: Dict[str, str] = {}, 
-                 out_dir: Optional[os.PathLike] = None,
-                 prev_method: Optional[MethodWrapper] = None,
-                 loader: Optional[LoaderInterface] = None,
-                 **kwargs):
-        super().__init__(method_repository, module_path, method_name, comm, save_result, output_mapping, **kwargs)
+
+    def __init__(
+        self,
+        method_repository: MethodRepository,
+        module_path: str,
+        method_name: str,
+        comm: Comm,
+        save_result: Optional[bool] = None,
+        output_mapping: Dict[str, str] = {},
+        out_dir: Optional[os.PathLike] = None,
+        prev_method: Optional[MethodWrapper] = None,
+        loader: Optional[LoaderInterface] = None,
+        **kwargs,
+    ):
+        super().__init__(
+            method_repository,
+            module_path,
+            method_name,
+            comm,
+            save_result,
+            output_mapping,
+            **kwargs,
+        )
         assert loader is not None
         self._loader = loader
         assert prev_method is not None
@@ -40,14 +49,16 @@ def __init__(self,
         filename = f"{prev_method.task_id}-{prev_method.package_name}-{prev_method.method_name}"
         if prev_method.recon_algorithm is not None:
             filename += f"-{prev_method.recon_algorithm}"
-        
+
         if out_dir is None:
             out_dir = httomo.globals.run_out_dir
         assert out_dir is not None
-        self._file = h5py.File(f"{out_dir}/{filename}.h5", "w", driver="mpio", comm=comm)
+        self._file = h5py.File(
+            f"{out_dir}/{filename}.h5", "w", driver="mpio", comm=comm
+        )
         # make sure file gets closed properly
         weakref.finalize(self, self._file.close)
-        
+
     def execute(self, block: T) -> T:
         # we overwrite the whole execute method here, as we do not need any of the helper
         # methods from the Generic Wrapper
@@ -74,7 +85,7 @@ def execute(self, block: T) -> T:
             detector_y=self._loader.detector_y,
             angles=block.angles,
         )
-        
+
         if block.is_last_in_chunk:
             self._file.close()
 
diff --git a/httomo/method_wrappers/stats_calc.py b/httomo/method_wrappers/stats_calc.py
index 0db3bcccb..ee4900b53 100644
--- a/httomo/method_wrappers/stats_calc.py
+++ b/httomo/method_wrappers/stats_calc.py
@@ -1,6 +1,5 @@
 from httomo.block_interfaces import T
 from httomo.method_wrappers.generic import GenericMethodWrapper
-from httomo.runner.dataset import DataSetBlock
 from httomo.runner.methods_repository_interface import MethodRepository
 from httomo.utils import catchtime, log_rank, xp, gpu_enabled
 
@@ -50,9 +49,9 @@ def __init__(
     def _transfer_data(self, dataset: T) -> T:
         # don't transfer anything (either way) at this point
         return dataset
-    
+
     def _run_method(self, dataset: T, args: Dict[str, Any]) -> T:
-        # transfer data to GPU if we can / have it available (always faster), 
+        # transfer data to GPU if we can / have it available (always faster),
         # but don't want to fail if we don't have a GPU (underlying method works for both)
         # and don't touch original dataset
         if gpu_enabled and dataset.is_cpu:
@@ -61,11 +60,8 @@ def _run_method(self, dataset: T, args: Dict[str, Any]) -> T:
             self._gpu_time_info.host2device += t.elapsed
         ret = self._method(**args)
         return self._process_return_type(ret, dataset)
-        
 
-    def _process_return_type(
-        self, ret: Any, input_block: T
-    ) -> T:
+    def _process_return_type(self, ret: Any, input_block: T) -> T:
         assert isinstance(ret, tuple), "expected return type is a tuple"
         assert len(ret) == 4, "A 4-tuple of stats values is expected"
 
diff --git a/httomo/methods_database/packages/external/httomolibgpu/httomolibgpu.yaml b/httomo/methods_database/packages/external/httomolibgpu/httomolibgpu.yaml
index 397a0246a..86c8d59c1 100644
--- a/httomo/methods_database/packages/external/httomolibgpu/httomolibgpu.yaml
+++ b/httomo/methods_database/packages/external/httomolibgpu/httomolibgpu.yaml
@@ -51,7 +51,7 @@ prep:
   normalize:
     normalize:
       pattern: projection
-      output_dims_change: False      
+      output_dims_change: False
       implementation: gpu_cupy
       save_result_default: False
       padding: False
@@ -62,7 +62,7 @@ prep:
   phase:
     paganin_filter_tomopy:
       pattern: projection
-      output_dims_change: False      
+      output_dims_change: False
       implementation: gpu_cupy
       save_result_default: False
       padding: False
@@ -72,7 +72,7 @@ prep:
         - methods: [module]
     paganin_filter_savu:
       pattern: projection
-      output_dims_change: False      
+      output_dims_change: False
       implementation: gpu_cupy
       save_result_default: False
       padding: False
@@ -83,7 +83,7 @@ prep:
   alignment:
     distortion_correction_proj_discorpy:
       pattern: projection
-      output_dims_change: False      
+      output_dims_change: False
       implementation: gpu_cupy
       save_result_default: False
       padding: False
@@ -94,7 +94,7 @@ prep:
   stripe:
     remove_stripe_based_sorting:
       pattern: sinogram
-      output_dims_change: False      
+      output_dims_change: False
       implementation: gpu_cupy
       save_result_default: False
       padding: False
diff --git a/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/misc/rescale.py b/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/misc/rescale.py
index b44ebf17e..2e0ef590e 100644
--- a/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/misc/rescale.py
+++ b/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/misc/rescale.py
@@ -7,9 +7,9 @@
 
 
 def _calc_memory_bytes_rescale_to_int(
-        non_slice_dims_shape: Tuple[int, int],
-        dtype: np.dtype,
-        **kwargs,
+    non_slice_dims_shape: Tuple[int, int],
+    dtype: np.dtype,
+    **kwargs,
 ) -> Tuple[int, int]:
     bits: int = kwargs["bits"]
     if bits == 8:
@@ -19,4 +19,7 @@ def _calc_memory_bytes_rescale_to_int(
     else:
         itemsize = 4
     safety = 128
-    return (int(np.prod(non_slice_dims_shape)) * (dtype.itemsize + itemsize) + safety, 0)
+    return (
+        int(np.prod(non_slice_dims_shape)) * (dtype.itemsize + itemsize) + safety,
+        0,
+    )
diff --git a/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/prep/normalize.py b/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/prep/normalize.py
index 261ea6380..6b32d8c9b 100644
--- a/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/prep/normalize.py
+++ b/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/prep/normalize.py
@@ -6,18 +6,19 @@
     "_calc_memory_bytes_normalize",
 ]
 
+
 def _calc_memory_bytes_normalize(
-        non_slice_dims_shape: Tuple[int, int],
-        dtype: np.dtype,
-        **kwargs,
+    non_slice_dims_shape: Tuple[int, int],
+    dtype: np.dtype,
+    **kwargs,
 ) -> Tuple[int, int]:
-     # this function changes the data type
+    # this function changes the data type
     in_slice_mem = np.prod(non_slice_dims_shape) * dtype.itemsize
     out_slice_mem = np.prod(non_slice_dims_shape) * np.float32().itemsize
-    
+
     # fixed cost for keeping mean of flats and darks
     mean_mem = int(np.prod(non_slice_dims_shape) * np.float32().itemsize * 2)
 
     tot_memory_bytes = int(in_slice_mem + out_slice_mem)
 
-    return (tot_memory_bytes, mean_mem)
\ No newline at end of file
+    return (tot_memory_bytes, mean_mem)
diff --git a/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/recon/algorithm.py b/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/recon/algorithm.py
index a8ef90e96..757b8fce4 100644
--- a/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/recon/algorithm.py
+++ b/httomo/methods_database/packages/external/httomolibgpu/supporting_funcs/recon/algorithm.py
@@ -72,7 +72,7 @@ def _calc_memory_bytes_FBP(
 
     # 1. input
     input_slice_size = np.prod(non_slice_dims_shape) * dtype.itemsize
-    
+
     ########## FFT / filter / IFFT (filtersync_cupy)
 
     # 2. RFFT plan (R2C transform)
@@ -84,12 +84,10 @@ def _calc_memory_bytes_FBP(
         )
         / SLICES
     )
-    
+
     # 3. RFFT output size (proj_f in code)
-    proj_f_slice = (
-        det_height * (det_width // 2 + 1) * np.complex64().itemsize
-    )
-    
+    proj_f_slice = det_height * (det_width // 2 + 1) * np.complex64().itemsize
+
     # 4. Filter size (independent of number of slices)
     filter_size = (det_width // 2 + 1) * np.float32().itemsize
 
@@ -102,54 +100,62 @@ def _calc_memory_bytes_FBP(
         )
         / SLICES
     )
-    
+
     # 6. output of filtersync call
     filtersync_output_slice_size = input_slice_size
-    
+
     # since the FFT plans, proj_f, and input data is dropped after the filtersync call, we track it here
     # separate
-    filtersync_size = input_slice_size + fftplan_slice_size + proj_f_slice + ifftplan_slice_size
-    
+    filtersync_size = (
+        input_slice_size + fftplan_slice_size + proj_f_slice + ifftplan_slice_size
+    )
+
     # 6. we swap the axes before passing data to Astra in ToMoBAR
     # https://github.com/dkazanc/ToMoBAR/blob/54137829b6326406e09f6ef9c95eb35c213838a7/tomobar/methodsDIR_CuPy.py#L135
-    pre_astra_input_swapaxis_slice = np.prod(non_slice_dims_shape) * np.float32().itemsize
-    
-    # 7. astra backprojection will generate an output array 
+    pre_astra_input_swapaxis_slice = (
+        np.prod(non_slice_dims_shape) * np.float32().itemsize
+    )
+
+    # 7. astra backprojection will generate an output array
     # https://github.com/dkazanc/ToMoBAR/blob/54137829b6326406e09f6ef9c95eb35c213838a7/tomobar/astra_wrappers/astra_base.py#L524
     output_dims = _calc_output_dim_FBP(non_slice_dims_shape, **kwargs)
     recon_output_size = np.prod(output_dims) * np.float32().itemsize
-    
+
     # 7. astra backprojection makes a copy of the input
     astra_input_slice_size = np.prod(non_slice_dims_shape) * np.float32().itemsize
-    
+
     ## now we calculate back projection memory (2 copies of the input + reconstruction output)
-    projection_mem_size = pre_astra_input_swapaxis_slice + astra_input_slice_size + recon_output_size
-    
+    projection_mem_size = (
+        pre_astra_input_swapaxis_slice + astra_input_slice_size + recon_output_size
+    )
+
     # 9. apply_circular_mask memory (fixed amount, not per slice)
     circular_mask_size = np.prod(output_dims) * np.int64().itemsize
-    
+
     fixed_amount = max(filter_size, circular_mask_size)
-    
+
     # 9. this swapaxis makes another copy of the output data
     # https://github.com/DiamondLightSource/httomolibgpu/blob/72d98ec7ac44e06ee0318043934fb3f68667d203/httomolibgpu/recon/algorithm.py#L118
     # BUT: this swapaxis happens after the cudaArray inputs and the input swapaxis arrays are dropped,
     #      so it does not add to the memory overall
-    
+
     # NOTE
     # Although the assumption that the memory will be cleared after filtration step (the fft plans)
     # does hold for some WSs, on the cluster it results in OOM error in the IFFT step.
     # It is not very clear why it is the case so far, therefore the workaround is to account for all memory required
-    # for the FBP step (filtering part + ASTRA backprojection). This is not ideal as it uses a lot of memory 
-    # and therefore the blocks will be smaller making I/O suboptimal. 
+    # for the FBP step (filtering part + ASTRA backprojection). This is not ideal as it uses a lot of memory
+    # and therefore the blocks will be smaller making I/O suboptimal.
 
-    # The commented code bellow is how the memory should be estimated in principle 
+    # The commented code bellow is how the memory should be estimated in principle
     # if projection_mem_size > filtersync_size:
     #     tot_memory_bytes = int(filtersync_output_slice_size + projection_mem_size)
     # else:
     #     tot_memory_bytes = int(filtersync_output_slice_size + filtersync_size + recon_output_size)
-    
-    # this account for the memory used for filtration AND backprojection. 
-    tot_memory_bytes = int(filtersync_output_slice_size + filtersync_size + projection_mem_size)
+
+    # this account for the memory used for filtration AND backprojection.
+    tot_memory_bytes = int(
+        filtersync_output_slice_size + filtersync_size + projection_mem_size
+    )
 
     return (tot_memory_bytes, fixed_amount)
 
diff --git a/httomo/methods_database/packages/external/tomopy/supporting_funcs/misc/corr.py b/httomo/methods_database/packages/external/tomopy/supporting_funcs/misc/corr.py
index 2afc94f7d..4d5dda20e 100644
--- a/httomo/methods_database/packages/external/tomopy/supporting_funcs/misc/corr.py
+++ b/httomo/methods_database/packages/external/tomopy/supporting_funcs/misc/corr.py
@@ -34,5 +34,3 @@ def _calc_padding_median_filter3d(size: int = 3, **kwargs) -> Tuple[int, int]:
 
 def _calc_padding_remove_outlier3d(size: int = 3, **kwargs) -> Tuple[int, int]:
     return size // 2, size // 2
-
-
diff --git a/httomo/methods_database/packages/external/tomopy/supporting_funcs/recon/algorithm.py b/httomo/methods_database/packages/external/tomopy/supporting_funcs/recon/algorithm.py
index 84fffa08b..6908d1cbf 100644
--- a/httomo/methods_database/packages/external/tomopy/supporting_funcs/recon/algorithm.py
+++ b/httomo/methods_database/packages/external/tomopy/supporting_funcs/recon/algorithm.py
@@ -28,9 +28,9 @@
     "_calc_output_dim_recon",
 ]
 
+
 def _calc_output_dim_recon(non_slice_dims_shape, **kwargs):
-    """Function to calculate output dimensions for all reconstructors. 
-    """
+    """Function to calculate output dimensions for all reconstructors."""
     DetectorsLengthH = non_slice_dims_shape[1]
     output_dims = (DetectorsLengthH, DetectorsLengthH)
-    return output_dims
\ No newline at end of file
+    return output_dims
diff --git a/httomo/methods_database/packages/httomo.yaml b/httomo/methods_database/packages/httomo.yaml
index 9bf6a5fa5..2841a5abb 100644
--- a/httomo/methods_database/packages/httomo.yaml
+++ b/httomo/methods_database/packages/httomo.yaml
@@ -5,9 +5,9 @@ data:
         pattern: projection
         output_dims_change: False
         implementation: cpu
-        memory_gpu: None 
+        memory_gpu: None
         save_result_default: False
-methods:  
+methods:
   calculate_stats:
     pattern: all
     output_dims_change: False
diff --git a/httomo/methods_database/query.py b/httomo/methods_database/query.py
index fe3023fe8..0c7a5e9aa 100644
--- a/httomo/methods_database/query.py
+++ b/httomo/methods_database/query.py
@@ -96,11 +96,9 @@ def save_result_default(self) -> bool:
         return get_method_info(
             self.module_path, self.method_name, "save_result_default"
         )
-        
+
     def padding(self) -> bool:
-        return get_method_info(
-            self.module_path, self.method_name, "padding"
-        )
+        return get_method_info(self.module_path, self.method_name, "padding")
 
     def get_memory_gpu_params(
         self,
@@ -144,7 +142,7 @@ def calculate_output_dims(
         smodule = self._import_supporting_funcs_module()
         module_mem: Callable = getattr(smodule, "_calc_output_dim_" + self.method_name)
         return module_mem(non_slice_dims_shape, **kwargs)
-    
+
     def calculate_padding(self, **kwargs) -> Tuple[int, int]:
         smodule = self._import_supporting_funcs_module()
         module_pad: Callable = getattr(smodule, "_calc_padding_" + self.method_name)
diff --git a/httomo/monitors/__init__.py b/httomo/monitors/__init__.py
index 48edff3d6..8d848b337 100644
--- a/httomo/monitors/__init__.py
+++ b/httomo/monitors/__init__.py
@@ -8,10 +8,8 @@
 from httomo.runner.monitoring_interface import MonitoringInterface
 
 
-MONITORS_MAP = {
-    "bench": BenchmarkMonitoring,
-    "summary": SummaryMonitor
-}
+MONITORS_MAP = {"bench": BenchmarkMonitoring, "summary": SummaryMonitor}
+
 
 def make_monitors(
     monitor_descriptors: List[str],
@@ -19,11 +17,13 @@ def make_monitors(
 ) -> Optional[MonitoringInterface]:
     if len(monitor_descriptors) == 0:
         return None
-    
+
     monitors: List[MonitoringInterface] = []
     for descriptor in monitor_descriptors:
         if descriptor not in MONITORS_MAP:
-            raise ValueError(f"Unknown monitor '{descriptor}'. Please choose one of {MONITORS_MAP.keys()}")
+            raise ValueError(
+                f"Unknown monitor '{descriptor}'. Please choose one of {MONITORS_MAP.keys()}"
+            )
         monitors.append(MONITORS_MAP[descriptor](comm))
-        
+
     return AggregateMonitoring(monitors)
diff --git a/httomo/monitors/aggregate.py b/httomo/monitors/aggregate.py
index 456bc38bc..cc0c9a23d 100644
--- a/httomo/monitors/aggregate.py
+++ b/httomo/monitors/aggregate.py
@@ -47,7 +47,13 @@ def report_source_block(
     ):
         for m in self._monitors:
             m.report_source_block(
-                name, first_task_id, slicing_dim, block_dims, block_idx_chunk, block_idx_global, cpu_time
+                name,
+                first_task_id,
+                slicing_dim,
+                block_dims,
+                block_idx_chunk,
+                block_idx_global,
+                cpu_time,
             )
 
     def report_sink_block(
@@ -62,16 +68,21 @@ def report_sink_block(
     ):
         for m in self._monitors:
             m.report_sink_block(
-                name, last_task_id, slicing_dim, block_dims, block_idx_chunk, block_idx_global, cpu_time
+                name,
+                last_task_id,
+                slicing_dim,
+                block_dims,
+                block_idx_chunk,
+                block_idx_global,
+                cpu_time,
             )
-            
+
     def report_total_time(self, cpu_time: float):
         for m in self._monitors:
             m.report_total_time(cpu_time)
-    
 
     def write_results(self, dest: TextIO):
         for m in self._monitors:
             m.write_results(dest)
             dest.writelines("\n")
-            dest.flush()
\ No newline at end of file
+            dest.flush()
diff --git a/httomo/monitors/benchmark.py b/httomo/monitors/benchmark.py
index 9db9b3de4..b286bfc3f 100644
--- a/httomo/monitors/benchmark.py
+++ b/httomo/monitors/benchmark.py
@@ -23,7 +23,7 @@ def report_method_block(
         cpu_time: float,
         gpu_kernel_time: float = 0.0,
         gpu_h2d_time: float = 0.0,
-        gpu_d2h_time: float = 0.0
+        gpu_d2h_time: float = 0.0,
     ):
         self._data.append(
             OrderedDict(
@@ -140,7 +140,7 @@ def write_results(self, dest: TextIO):
             writer = csv.DictWriter(dest, fieldnames=self._data[0].keys())
             writer.writeheader()
             writer.writerows(self._data)
-        
+
     def _aggregate_mpi(self):
         alldata = self._comm.gather(self._data)
         if self._comm.rank == 0:
diff --git a/httomo/monitors/summary.py b/httomo/monitors/summary.py
index b2c974b0e..87f6a1d4f 100644
--- a/httomo/monitors/summary.py
+++ b/httomo/monitors/summary.py
@@ -15,7 +15,7 @@ def __init__(self, comm: MPI.Comm) -> None:
         self._sinks = 0.0
         self._total = 0.0
         self._total_agg = 0.0
-    
+
     def report_method_block(
         self,
         method_name: str,
@@ -28,7 +28,7 @@ def report_method_block(
         cpu_time: float,
         gpu_kernel_time: float = 0.0,
         gpu_h2d_time: float = 0.0,
-        gpu_d2h_time: float = 0.0        
+        gpu_d2h_time: float = 0.0,
     ):
         if method_name not in self._methods:
             self._methods[method_name] = 0.0
@@ -37,7 +37,7 @@ def report_method_block(
         self._methods_gpu += gpu_kernel_time
         self._h2d += gpu_h2d_time
         self._d2h += gpu_d2h_time
-        
+
     def report_source_block(
         self,
         name: str,
@@ -46,10 +46,10 @@ def report_source_block(
         block_dims: Tuple[int, int, int],
         block_idx_chunk: Tuple[int, int, int],
         block_idx_global: Tuple[int, int, int],
-        cpu_time: float
+        cpu_time: float,
     ):
         self._sources += cpu_time
-        
+
     def report_sink_block(
         self,
         name: str,
@@ -58,13 +58,13 @@ def report_sink_block(
         block_dims: Tuple[int, int, int],
         block_idx_chunk: Tuple[int, int, int],
         block_idx_global: Tuple[int, int, int],
-        cpu_time: float
+        cpu_time: float,
     ):
         self._sinks += cpu_time
-        
+
     def report_total_time(self, cpu_time: float):
         self._total = cpu_time
-        
+
     def write_results(self, dest: TextIO):
         self._aggregate_mpi()
         if self._comm.rank == 0:
@@ -83,10 +83,12 @@ def write_results(self, dest: TextIO):
                 f"  ------------------------" + "-" * 15,
                 f"Method breakdowns:",
             ]
-            for k,v in self._methods.items():
-                lines.append(f"  {k:>30s} : {v:>10.3f}s ({v / self._total * 100:>4.1f}%)")
-            dest.write('\n'.join(lines))
-        
+            for k, v in self._methods.items():
+                lines.append(
+                    f"  {k:>30s} : {v:>10.3f}s ({v / self._total * 100:>4.1f}%)"
+                )
+            dest.write("\n".join(lines))
+
     def _aggregate_mpi(self):
         self._total_agg = self._total
         if self._comm.size == 1:
diff --git a/httomo/runner/auxiliary_data.py b/httomo/runner/auxiliary_data.py
index ae1612ab1..6dd134af1 100644
--- a/httomo/runner/auxiliary_data.py
+++ b/httomo/runner/auxiliary_data.py
@@ -3,19 +3,23 @@
 import numpy as np
 from httomo.utils import xp, gpu_enabled
 
+
 class AuxiliaryData:
     """
     Keeps the darks and flats and angles auxiliary data together, and separate from the dataset.
-    
+
     This allows them to be updated on their own without being affected by chunks and blocks, etc.,
     including the GPU/CPU transfers if needed.
     """
-    
+
     generic_array: TypeAlias = Union[xp.ndarray, np.ndarray]
-    
-    def __init__(self,  angles: np.ndarray, 
-                 darks: Optional[np.ndarray] = None, 
-                 flats: Optional[np.ndarray] = None):
+
+    def __init__(
+        self,
+        angles: np.ndarray,
+        darks: Optional[np.ndarray] = None,
+        flats: Optional[np.ndarray] = None,
+    ):
         self._darks: Optional[AuxiliaryData.generic_array] = darks
         self._flats: Optional[AuxiliaryData.generic_array] = flats
         self._angles: np.ndarray = angles
@@ -23,71 +27,70 @@ def __init__(self,  angles: np.ndarray,
     @property
     def darks_dtype(self) -> Optional[np.dtype]:
         return self._darks.dtype if self._darks is not None else None
-    
+
     @property
     def darks_shape(self) -> Tuple[int, int, int]:
         if self._darks is None:
             return (0, 0, 0)
         assert len(self._darks.shape) == 3
         return (self._darks.shape[0], self._darks.shape[1], self._darks.shape[2])
-    
+
     @property
     def flats_dtype(self) -> Optional[np.dtype]:
         return self._flats.dtype if self._flats is not None else None
-    
+
     @property
     def flats_shape(self) -> Tuple[int, int, int]:
         if self._flats is None:
             return (0, 0, 0)
         assert len(self._flats.shape) == 3
         return (self._flats.shape[0], self._flats.shape[1], self._flats.shape[2])
-    
+
     @property
     def angles_dtype(self) -> np.dtype:
         return self._angles.dtype
-    
+
     @property
     def angles_length(self) -> int:
         return len(self._angles)
-    
-        
+
     def get_darks(self, gpu=False) -> Optional[generic_array]:
         return self._get_field("darks", gpu)
-    
+
     def get_flats(self, gpu=False) -> Optional[generic_array]:
         return self._get_field("flats", gpu)
-    
+
     def get_angles(self) -> np.ndarray:
         return self._angles
-    
+
     def set_darks(self, darks: generic_array) -> None:
         self._darks = darks
-        
+
     def set_flats(self, flats: generic_array) -> None:
         self._flats = flats
-        
+
     def set_angles(self, angles: np.ndarray) -> None:
         assert getattr(angles, "device", None) is None, "Angles must be a CPU array"
         self._angles = angles
-        
+
     def _get_field(self, field: str, gpu=False) -> generic_array:
         assert not gpu or gpu_enabled, "GPU can only be used if the GPU is enabled"
-        
+
         array = getattr(self, f"_{field}")
         if array is None:
             return array
-        
+
         # Note: if already on CPU/GPU, no copy is taken
         if gpu:
             array = xp.asarray(array)
         else:
-            if xp.__name__ == 'cupy':
+            if xp.__name__ == "cupy":
                 array = xp.asnumpy(array)
-        
+
         setattr(self, f"_{field}", array)
-        
+
         return array
-    
+
     def drop_darks_flats(self):
         self._darks = None
-        self._flats = None
\ No newline at end of file
+        self._flats = None
diff --git a/httomo/runner/block_split.py b/httomo/runner/block_split.py
index e9bdc7d38..fc2c1ff35 100644
--- a/httomo/runner/block_split.py
+++ b/httomo/runner/block_split.py
@@ -6,6 +6,7 @@
 
 log = logging.getLogger(__name__)
 
+
 class BlockSplitter:
     """Produces blocks from a DataSetSource according to the given max slices
     per block. It provides an iterator interface, so that it can be used as::
@@ -48,8 +49,8 @@ class BlockIterator:
             def __init__(self, splitter):
                 self.splitter = splitter
                 self._current = 0
-                
-            def __iter__(self) -> 'BlockIterator':
+
+            def __iter__(self) -> "BlockIterator":
                 return self  # pragma: no cover
 
             def __next__(self) -> DataSetBlock:
@@ -60,4 +61,3 @@ def __next__(self) -> DataSetBlock:
                 return v
 
         return BlockIterator(self)
-
diff --git a/httomo/runner/dataset.py b/httomo/runner/dataset.py
index 8df15ac90..95a0e2596 100644
--- a/httomo/runner/dataset.py
+++ b/httomo/runner/dataset.py
@@ -241,6 +241,7 @@ def data_unpadded(self) -> generic_array:
             return self.data
         d = self.data
         slices = [slice(None), slice(None), slice(None)]
-        slices[self.slicing_dim] = slice(self.padding[0], d.shape[self.slicing_dim] - self.padding[1])
+        slices[self.slicing_dim] = slice(
+            self.padding[0], d.shape[self.slicing_dim] - self.padding[1]
+        )
         return d[slices[0], slices[1], slices[2]]
-
diff --git a/httomo/runner/dataset_store_interfaces.py b/httomo/runner/dataset_store_interfaces.py
index 1a5d583c1..083e56f3a 100644
--- a/httomo/runner/dataset_store_interfaces.py
+++ b/httomo/runner/dataset_store_interfaces.py
@@ -54,7 +54,7 @@ class DataSetSource(Protocol):
     @property
     def dtype(self) -> np.dtype:
         ...  # pragma: no cover
-    
+
     @property
     def global_shape(self) -> Tuple[int, int, int]:
         """Global data shape across all processes that we eventually have to read."""
@@ -75,12 +75,12 @@ def global_index(self) -> Tuple[int, int, int]:
     def slicing_dim(self) -> Literal[0, 1, 2]:
         """Slicing dimension - 0, 1, or 2"""
         ...  # pragma: no cover
-        
+
     @property
     def aux_data(self) -> AuxiliaryData:
         """Auxiliary data"""
         ...  # pragma: no cover
-        
+
     def read_block(self, start: int, length: int) -> DataSetBlock:
         """Reads a block from the dataset, starting at `start` of length `length`,
         in the current slicing dimension. Note that `start` is chunk-based,
@@ -128,11 +128,12 @@ def finalize(self):
         to give implementations a chance to write everything to disk and close the file,
         etc."""
         ...  # pragma: no cover
-        
+
+
 class ReadableDataSetSink(DataSetSink):
     """Interface for a DataSetSink that is store-based, i.e. where it's possible to construct
-       a reader from the same data store that this sink has been written first"""
-       
+    a reader from the same data store that this sink has been written first"""
+
     @abc.abstractmethod
     def make_reader(
         self, new_slicing_dim: Optional[Literal[0, 1, 2]] = None
diff --git a/httomo/runner/gpu_utils.py b/httomo/runner/gpu_utils.py
index f0d41317a..d82ba0d1c 100644
--- a/httomo/runner/gpu_utils.py
+++ b/httomo/runner/gpu_utils.py
@@ -9,8 +9,8 @@ def gpumem_cleanup():
         xp.get_default_memory_pool().free_all_blocks()
         cache = xp.fft.config.get_plan_cache()
         cache.clear()
-    
-        
+
+
 def get_available_gpu_memory(safety_margin_percent: float = 10.0) -> int:
     try:
         import cupy as cp
diff --git a/httomo/runner/method_wrapper.py b/httomo/runner/method_wrapper.py
index a315aa548..6da4063a5 100644
--- a/httomo/runner/method_wrapper.py
+++ b/httomo/runner/method_wrapper.py
@@ -7,38 +7,55 @@
 from mpi4py import MPI
 
 import os
-from typing import Any, Callable, Dict, List, Literal, Optional, Protocol, Tuple, TypeVar, Union
-
-MethodParameterValues = Union[str, bool, int, float, os.PathLike, np.ndarray, xp.ndarray]
-MethodParameterDictType = Dict[str, Union[MethodParameterValues, List[MethodParameterValues]]]
+from typing import (
+    Any,
+    Callable,
+    Dict,
+    List,
+    Literal,
+    Optional,
+    Protocol,
+    Tuple,
+    TypeVar,
+    Union,
+)
+
+MethodParameterValues = Union[
+    str, bool, int, float, os.PathLike, np.ndarray, xp.ndarray
+]
+MethodParameterDictType = Dict[
+    str, Union[MethodParameterValues, List[MethodParameterValues]]
+]
 
 
 @dataclass
 class GpuTimeInfo:
     """Captures the time spent on GPU"""
+
     kernel: float = 0.0
     host2device: float = 0.0
     device2host: float = 0.0
 
+
 class MethodWrapper(Protocol):
     """Interface for method wrappers, that is used by the pipeline and task runners to execute
     methods in a generic way."""
-    
+
     # read/write properties
     task_id: str
     pattern: Pattern
-    
+
     # read-only properties
     @property
     def comm(self) -> MPI.Comm:
         """The MPI communicator used"""
         ...  # pragma: no cover
-    
+
     @property
     def method(self) -> Callable:
         """The actual method underlying this wrapper"""
         ...  # pragma: no cover
-    
+
     @property
     def parameters(self) -> List[str]:
         """List of parameter names of the underlying method"""
@@ -48,88 +65,86 @@ def parameters(self) -> List[str]:
     def memory_gpu(self) -> List[GpuMemoryRequirement]:
         """Memory requirements for GPU execution"""
         ...  # pragma: nocover
-        
+
     @property
     def implementation(self) -> Literal["gpu", "cpu", "gpu_cupy"]:
         """Implementation of this method"""
         ...  # pragma: nocover
-        
+
     @property
     def output_dims_change(self) -> bool:
         """Whether output dimensions change after executing this method"""
         ...  # pragma: nocover
-    
+
     @property
     def save_result(self) -> bool:
         """Whether to save the result of this method to intermediate files"""
-        ... # pragma: nocover
-    
+        ...  # pragma: nocover
+
     @property
     def method_name(self) -> str:
         """Returns the name of the method function"""
-        ... # pragma: nocover
-    
+        ...  # pragma: nocover
+
     @property
     def module_path(self) -> str:
         """Returns the full module path where the method function is defined"""
-        ... # pragma: nocover
-    
+        ...  # pragma: nocover
+
     @property
     def package_name(self) -> str:
         """The name of the top-level package where this method is implementated, e.g. 'httomolib'"""
-        ... # pragma: nocover
-    
+        ...  # pragma: nocover
+
     @property
     def cupyrun(self) -> bool:
         """True if method runs on GPU and expects a CuPy array as inputs"""
-        ... # pragma: nocover
+        ...  # pragma: nocover
 
     @property
     def is_cpu(self) -> bool:
         """True if this is a CPU-only method"""
-        ... # pragma: nocover
+        ...  # pragma: nocover
 
     @property
     def is_gpu(self) -> bool:
         """True if this is a GPU method"""
-        ... # pragma: nocover
-    
+        ...  # pragma: nocover
+
     @property
     def gpu_time(self) -> GpuTimeInfo:
         """Get the time spent on GPU in the last call to execute"""
-        ... # pragma: nocover
-        
+        ...  # pragma: nocover
+
     @property
     def config_params(self) -> Dict[str, Any]:
         """Access a copy of the configuration parameters (cannot be modified directly)"""
-        ... # pragma: nocover
-        
+        ...  # pragma: nocover
+
     @property
     def recon_algorithm(self) -> Optional[str]:
         """Determine the recon algorithm used, if the method is reconstruction.
         Otherwise return None."""
-        ... # pragma: nocover
-        
+        ...  # pragma: nocover
+
     @property
     def padding(self) -> bool:
         """Deterime if the method needs padding"""
-        ... # pragma: nocover
+        ...  # pragma: nocover
 
     # Methods
 
     def __getitem__(self, key: str) -> MethodParameterValues:
         """Get a parameter for the method using dictionary notation (wrapper["param"])"""
-        ... # pragma: nocover
+        ...  # pragma: nocover
 
     def __setitem__(self, key: str, value: MethodParameterValues):
         """Set a parameter for the method using dictionary notation (wrapper["param"] = 42)"""
-        ... # pragma: nocover
-
+        ...  # pragma: nocover
 
     def append_config_params(self, params: MethodParameterDictType):
         """Appends to the configuration parameters all values that are in the given dictionary"""
-        ... # pragma: nocover
-
+        ...  # pragma: nocover
 
     # For a given type `T` that implements the `Block` protocol, the return type should be the
     # same `T` as the input `T` (rather than allowing the input implementor of `Block` and the
@@ -150,23 +165,23 @@ def execute(self, block: T) -> T:
         T (implements `Block`)
             A CPU or GPU-based dataset object with the output
         """
-        ... # pragma: nocover
-        
+        ...  # pragma: nocover
+
     def get_side_output(self) -> Dict[str, Any]:
         """Override this method for functions that have a side output. The returned dictionary
         will be merged with the dict_params parameter passed to execute for all methods that
         follow in the pipeline"""
-        ... # pragma: nocover
-        
+        ...  # pragma: nocover
+
     def calculate_output_dims(
         self, non_slice_dims_shape: Tuple[int, int]
     ) -> Tuple[int, int]:
         """Calculate the dimensions of the output for this method"""
-        ... # pragma: nocover
-        
+        ...  # pragma: nocover
+
     def calculate_padding(self) -> Tuple[int, int]:
         """Calculate the padding required by the method"""
-        ... # pragma: nocover
+        ...  # pragma: nocover
 
     def calculate_max_slices(
         self,
@@ -182,4 +197,4 @@ def calculate_max_slices(
         The available memory may have been adjusted for the methods that follow, in case
         something persists afterwards.
         """
-        ... # pragma: nocover
+        ...  # pragma: nocover
diff --git a/httomo/runner/monitoring_interface.py b/httomo/runner/monitoring_interface.py
index 24d6690ad..8ea5ada28 100644
--- a/httomo/runner/monitoring_interface.py
+++ b/httomo/runner/monitoring_interface.py
@@ -14,10 +14,10 @@ def report_method_block(
         cpu_time: float,
         gpu_kernel_time: float = 0.0,
         gpu_h2d_time: float = 0.0,
-        gpu_d2h_time: float = 0.0
+        gpu_d2h_time: float = 0.0,
     ):
-        ... # pragma: no cover
-        
+        ...  # pragma: no cover
+
     def report_source_block(
         self,
         name: str,
@@ -26,10 +26,10 @@ def report_source_block(
         block_dims: Tuple[int, int, int],
         block_idx_chunk: Tuple[int, int, int],
         block_idx_global: Tuple[int, int, int],
-        cpu_time: float
+        cpu_time: float,
     ):
-        ... # pragma: no cover
-        
+        ...  # pragma: no cover
+
     def report_sink_block(
         self,
         name: str,
@@ -38,12 +38,12 @@ def report_sink_block(
         block_dims: Tuple[int, int, int],
         block_idx_chunk: Tuple[int, int, int],
         block_idx_global: Tuple[int, int, int],
-        cpu_time: float
+        cpu_time: float,
     ):
-        ... # pragma: no cover
-        
+        ...  # pragma: no cover
+
     def report_total_time(self, cpu_time: float):
-        ... # pragma: no cover
-        
+        ...  # pragma: no cover
+
     def write_results(self, dest: TextIO):
-        ... # pragma: no cover
+        ...  # pragma: no cover
diff --git a/httomo/runner/pipeline.py b/httomo/runner/pipeline.py
index 8906ca6ad..59c08caa1 100644
--- a/httomo/runner/pipeline.py
+++ b/httomo/runner/pipeline.py
@@ -9,11 +9,7 @@ class Pipeline:
     """Represents a pipeline of methods, stored by their wrappers, and the loader.
     After creation, the pipeline is immutable."""
 
-    def __init__(
-        self,
-        loader: LoaderInterface,
-        methods: List[MethodWrapper]
-    ):
+    def __init__(self, loader: LoaderInterface, methods: List[MethodWrapper]):
         self._methods = methods
         self._loader = loader
 
diff --git a/httomo/runner/section.py b/httomo/runner/section.py
index aaf2adb1c..479f7a602 100644
--- a/httomo/runner/section.py
+++ b/httomo/runner/section.py
@@ -22,7 +22,7 @@ def __init__(
         max_slices: int,
         methods: List[MethodWrapper],
         is_last: bool = False,
-        padding: bool = False
+        padding: bool = False,
     ):
         self.pattern = pattern
         self.max_slices = max_slices
@@ -66,7 +66,7 @@ def references_previous_method(method: MethodWrapper) -> bool:
             if r.method in current_methods:
                 return True
         return False
-    
+
     def is_second_padded_method(method: MethodWrapper) -> bool:
         return has_padding_method and method.padding
 
@@ -76,9 +76,11 @@ def finish_section():
             sections[-1].padding = True
 
     for method in pipeline:
-        if not is_pattern_compatible(
-            current_pattern, method.pattern
-        ) or references_previous_method(method) or is_second_padded_method(method):
+        if (
+            not is_pattern_compatible(current_pattern, method.pattern)
+            or references_previous_method(method)
+            or is_second_padded_method(method)
+        ):
             finish_section()
             has_padding_method = False
             if method.pattern != Pattern.all:
diff --git a/httomo/runner/task_runner.py b/httomo/runner/task_runner.py
index f5721c142..f1c09b250 100644
--- a/httomo/runner/task_runner.py
+++ b/httomo/runner/task_runner.py
@@ -362,12 +362,8 @@ def calculate_next_chunk_shape(
     """
     Utility function for calculating the chunk shape (including padding) for the next section.
     """
-    start = round(
-        (global_shape[next_section_slicing_dim] / comm.size) * comm.rank
-    )
-    stop = round(
-        (global_shape[next_section_slicing_dim] / comm.size) * (comm.rank + 1)
-    )
+    start = round((global_shape[next_section_slicing_dim] / comm.size) * comm.rank)
+    stop = round((global_shape[next_section_slicing_dim] / comm.size) * (comm.rank + 1))
     next_section_slicing_dim_len = stop - start
     shape = list(global_shape)
     shape[next_section_slicing_dim] = (
diff --git a/httomo/sweep_runner/param_sweep_runner.py b/httomo/sweep_runner/param_sweep_runner.py
index 4ecf4a30c..461b8af70 100644
--- a/httomo/sweep_runner/param_sweep_runner.py
+++ b/httomo/sweep_runner/param_sweep_runner.py
@@ -116,7 +116,7 @@ def prepare(self):
         sweep_block = ParamSweepBlock(
             data=dataset_block.data,
             aux_data=dataset_block.aux_data,
-            slicing_dim=SINO_SLICING_DIM
+            slicing_dim=SINO_SLICING_DIM,
         )
         self._block = sweep_block
 
@@ -162,7 +162,7 @@ def execute_sweep(self):
             block = ParamSweepBlock(
                 data=self.block.data.copy(),
                 aux_data=self.block.aux_data,
-                slicing_dim=self.block.slicing_dim
+                slicing_dim=self.block.slicing_dim,
             )
             self._side_output_manager.update_params(method)
             method.append_config_params({self._stages.sweep.param_name: val})
diff --git a/httomo/sweep_runner/param_sweep_store_interfaces.py b/httomo/sweep_runner/param_sweep_store_interfaces.py
index 687e442ce..af5356ca9 100644
--- a/httomo/sweep_runner/param_sweep_store_interfaces.py
+++ b/httomo/sweep_runner/param_sweep_store_interfaces.py
@@ -8,7 +8,6 @@
 class ParamSweepSource(Protocol):
     """Source for reading parameter sweep results from"""
 
-
     @property
     def no_of_sweeps(self) -> int:
         """Number of parameter values used to generate sweep results"""
diff --git a/httomo/transform_loader_params.py b/httomo/transform_loader_params.py
index 1432d3be5..834c472f5 100644
--- a/httomo/transform_loader_params.py
+++ b/httomo/transform_loader_params.py
@@ -57,7 +57,7 @@ def conv_param(par: Optional[PreviewParamEntry], length: int):
             start = 0 if val is None else val
             val = par.get("stop", None)
             stop = length if val is None else val
-            
+
         return PreviewDimConfig(start=start, stop=stop)
 
     return PreviewConfig(
diff --git a/httomo/ui_layer.py b/httomo/ui_layer.py
index bf9e58450..4f2a307b7 100644
--- a/httomo/ui_layer.py
+++ b/httomo/ui_layer.py
@@ -207,6 +207,7 @@ def update_side_output_references(
         check_valid_ref_id(side_str, ref_id, param_value, method)
         parameters[param_name] = OutputRef(method, ref_arg)
 
+
 def get_regex_pattern() -> re.Pattern:
     """Return the reference string regex pattern to search for
     Returns
diff --git a/httomo/utils.py b/httomo/utils.py
index 858c61421..a06fdadc9 100644
--- a/httomo/utils.py
+++ b/httomo/utils.py
@@ -1,5 +1,4 @@
 import logging
-from contextlib import contextmanager
 from enum import Enum
 from time import perf_counter_ns
 from typing import Any, Callable, Dict, List, Literal, Tuple
@@ -57,6 +56,7 @@ def log_once(output: Any, level: int = logging.INFO) -> None:
             else:
                 logger.info(output)
 
+
 def log_rank(output: Any, comm: MPI.Comm) -> None:
     """
     Log output to log file with the process rank.
diff --git a/httomo/yaml_checker.py b/httomo/yaml_checker.py
index 0a464cdb9..a44989727 100644
--- a/httomo/yaml_checker.py
+++ b/httomo/yaml_checker.py
@@ -435,9 +435,7 @@ def validate_yaml_config(yaml_file: Path, in_file: Optional[Path] = None) -> boo
     if not all_checks_pass:
         return False
 
-    end_str = (
-        "\nValidation of pipeline YAML file is successful."
-    )
+    end_str = "\nValidation of pipeline YAML file is successful."
     _print_with_colour(end_str, colour=Colour.BVIOLET)
     return True
 
diff --git a/pyproject.toml b/pyproject.toml
index 6b9dbb0cc..ca41a3d55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -79,6 +79,7 @@ dev-cpu = [
   "typing_extensions",
   "mypy",
   "types-PyYAML",
+  "pre-commit"
 ]
 dev-gpu = [
   "pytest",
@@ -96,6 +97,7 @@ dev-gpu = [
   "typing_extensions",
   "mypy",
   "types-PyYAML",
+  "pre-commit"
 ]
 
 [tool.mypy]
@@ -112,8 +114,8 @@ float_to_top = true
 # Make flake8 respect black's line length (default 88),
 max-line-length = 88
 extend-ignore = [
-    "E203",  
-    "F811"  
+    "E203",
+    "F811"
 ]
 per-file-ignores = [
     "*pyi:E302"


Test patch:
diff --git a/.github/workflows/cpu_tests.yml b/.github/workflows/cpu_tests.yml
deleted file mode 100644
index d5dcbd901..000000000
--- a/.github/workflows/cpu_tests.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: httomo CPU tests
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-    branches:
-      - main
-
-jobs:
-  build-linux:
-    runs-on: ubuntu-20.04
-
-    defaults:
-      run:
-        shell: bash -l {0}
-
-    steps:
-      - name: Checkout repository code
-        uses: actions/checkout@v2
-
-      # setup Python 3.9
-      - name: Setup Python 3.9
-        uses: actions/setup-python@v2
-        with:
-          python-version: 3.9
-
-      # install dependencies with conda
-      - name: Install dependencies with conda
-        run: |
-          conda env create --name httomo --file conda/environment-cpu.yml
-          conda activate httomo
-          conda run -n httomo pip install typing_extensions
-          conda run -n httomo pip install .
-          conda env list
-          conda list
-
-      # run tests
-      - name: Run CPU tests (No CUDA drivers needed)
-        run: |
-          conda run -n httomo pip install pytest plumbum
-          conda run -n httomo pytest -m "not cupy"
diff --git a/.github/workflows/main_conda_package_test.yml b/.github/workflows/main_conda_package_test.yml
new file mode 100644
index 000000000..88a07af9f
--- /dev/null
+++ b/.github/workflows/main_conda_package_test.yml
@@ -0,0 +1,50 @@
+name: Weekly conda package test
+
+on:
+  schedule:
+    - cron: '55 0 * * 1' # At 00:55 every Monday
+
+jobs:
+  download-test-iris:
+    runs-on: iris-gpu
+    container:
+      image: nvidia/cuda:11.6.2-devel-ubi8
+      env:
+        NVIDIA_VISIBLE_DEVICES: ${{ env.NVIDIA_VISIBLE_DEVICES }}
+
+    defaults:
+      run:
+        shell: bash -l {0}
+
+    steps:
+      - name: Checkout repository code
+        uses: actions/checkout@v4
+        with:
+          ref: "main"
+          fetch-depth: 0
+
+      - name: Create conda environment
+        uses: mamba-org/setup-micromamba@v1
+        with:
+          environment-file: conda/environment.yml
+          environment-name: httomo
+          post-cleanup: 'all'
+          init-shell: bash
+
+      - name: Install httomo
+        run: |
+          micromamba activate httomo
+          micromamba install "httomo/linux-64::httomo * py310_openmpi_regular*" -c conda-forge -c astra-toolbox -c rapidsai -y
+          micromamba list
+          
+      - name: Generate yaml templates
+        run: |
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/httomo_modules.yaml -o ./httomo/yaml_templates/httomo
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/tomopy/tomopy_modules.yaml -o ./httomo/yaml_templates/tomopy
+          python ./scripts/yaml_unsupported_tomopy_remove.py -t ./httomo/yaml_templates/tomopy -l ./httomo/methods_database/packages/external/tomopy/tomopy.yaml
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/httomolib/httomolib_modules.yaml -o ./httomo/yaml_templates/httomolib
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/httomolibgpu/httomolibgpu_modules.yaml -o ./httomo/yaml_templates/httomolibgpu          
+
+      - name: Run tests
+        run: |
+          pytest tests/
diff --git a/.github/workflows/run_tests_iris.yml b/.github/workflows/run_tests_iris.yml
new file mode 100644
index 000000000..6a44a71ec
--- /dev/null
+++ b/.github/workflows/run_tests_iris.yml
@@ -0,0 +1,51 @@
+name: HTTomo tests
+
+on:
+  pull_request:
+    branches:
+      - main
+  push:
+    branches:
+      - main
+
+jobs:
+  iris-gpu:
+    runs-on: iris-gpu
+    container:
+      image: nvidia/cuda:11.6.2-devel-ubi8
+      env:
+        NVIDIA_VISIBLE_DEVICES: ${{ env.NVIDIA_VISIBLE_DEVICES }}
+
+    defaults:
+      run:
+        shell: bash -l {0}
+
+    steps:
+      - name: Checkout repository code
+        uses: actions/checkout@v4
+
+      - name: Create conda environment
+        uses: mamba-org/setup-micromamba@v1
+        with:
+          environment-file: conda/environment.yml
+          environment-name: httomo
+          post-cleanup: 'all'
+          init-shell: bash
+          
+      - name: Install httomo-dev
+        run: |
+          micromamba activate httomo
+          pip install .[dev]
+          micromamba list
+                
+      - name: Generate yaml templates
+        run: |
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/httomo_modules.yaml -o ./httomo/yaml_templates/httomo
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/tomopy/tomopy_modules.yaml -o ./httomo/yaml_templates/tomopy
+          python ./scripts/yaml_unsupported_tomopy_remove.py -t ./httomo/yaml_templates/tomopy -l ./httomo/methods_database/packages/external/tomopy/tomopy.yaml
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/httomolib/httomolib_modules.yaml -o ./httomo/yaml_templates/httomolib
+          python ./scripts/yaml_templates_generator.py -i ./httomo/methods_database/packages/external/httomolibgpu/httomolibgpu_modules.yaml -o ./httomo/yaml_templates/httomolibgpu
+
+      - name: Run tests
+        run: |
+          pytest tests/
diff --git a/tests/conftest.py b/tests/conftest.py
index 66c8dc7c9..d002fd6c9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -302,7 +302,7 @@ def distortion_correction_path(test_data_path):
 def merge_yamls(load_yaml: Callable):
     def _merge_yamls(*yamls) -> None:
         """Merge multiple yaml files into one"""
-        data : List = []
+        data: List = []
         for y in yamls:
             curr_yaml_list = load_yaml(y)
             for x in curr_yaml_list:
@@ -336,7 +336,7 @@ def dummy_block() -> DataSetBlock:
 @pytest.fixture()
 def get_files():
     def _get_files(dir_path: str, excl: List[str] = []) -> List[str]:
-        """ Returns list of files from provided directory
+        """Returns list of files from provided directory
 
         Parameters
         ----------
@@ -354,13 +354,14 @@ def _get_files(dir_path: str, excl: List[str] = []) -> List[str]:
             str(f) for f in _dir if f.is_file() and not any(st in str(f) for st in excl)
         ]
         return _files
+
     return _get_files
 
 
 @pytest.fixture()
 def load_yaml():
     def _load_yaml(yaml_in: str) -> PipelineConfig:
-        """ Loads provided yaml and returns dict
+        """Loads provided yaml and returns dict
 
         Parameters
         ----------
@@ -374,5 +375,5 @@ def _load_yaml(yaml_in: str) -> PipelineConfig:
         with open(yaml_in, "r") as f:
             conf = list(yaml.load_all(f, Loader=yaml.FullLoader))
         return conf[0]
-    return _load_yaml
 
+    return _load_yaml
diff --git a/tests/data/test_param_sweep_store.py b/tests/data/test_param_sweep_store.py
index 56c9e95a1..5505fde5b 100644
--- a/tests/data/test_param_sweep_store.py
+++ b/tests/data/test_param_sweep_store.py
@@ -30,13 +30,13 @@ def test_param_sweep_writer_get_single_shape_before_write_raises_error():
         "Shape of single sweep result isn't known until the first write has occurred"
     ) in str(e)
 
+
 def test_param_sweep_writer_get_slices_per_sweep_before_write_raises_error():
     writer = make_param_sweep_writer()
     with pytest.raises(ValueError) as e:
         writer.slices_per_sweep
-    assert (
-        "Slices per sweep isn't known until the first write has occurred"
-    ) in str(e)
+    assert ("Slices per sweep isn't known until the first write has occurred") in str(e)
+
 
 def test_param_sweep_writer_get_total_shape_before_write_raises_error():
     writer = make_param_sweep_writer()
@@ -64,7 +64,7 @@ def test_param_sweep_writer_write_sweep_result_transfers_gpu_arr_to_cpu(
     block = ParamSweepBlock(
         data=np.ones(SWEEP_RES_SHAPE, dtype=np.float32),
         aux_data=AuxiliaryData(angles=np.ones(SWEEP_RES_SHAPE[0], dtype=np.float32)),
-        slicing_dim=1
+        slicing_dim=1,
     )
 
     # Patch `gpu_enabled` to simulate a GPU run
@@ -103,12 +103,12 @@ def test_param_sweep_writer_reader_write_res_and_read():
     sweep_result_1 = ParamSweepBlock(
         data=data[:, : SWEEP_RES_SHAPE[CONCAT_DIM], :],
         aux_data=aux_data,
-        slicing_dim=CONCAT_DIM
+        slicing_dim=CONCAT_DIM,
     )
     sweep_result_2 = ParamSweepBlock(
         data=data[:, SWEEP_RES_SHAPE[CONCAT_DIM] :, :],
         aux_data=aux_data,
-        slicing_dim=CONCAT_DIM
+        slicing_dim=CONCAT_DIM,
     )
 
     # Write two different blocks to the param sweep store, to simulate writing the results of a
diff --git a/tests/loaders/test_standard_tomo_loader.py b/tests/loaders/test_standard_tomo_loader.py
index 3fab37836..4ad32674c 100644
--- a/tests/loaders/test_standard_tomo_loader.py
+++ b/tests/loaders/test_standard_tomo_loader.py
@@ -15,6 +15,7 @@
 
 SlicingDimType = Literal[0, 1, 2]
 
+
 def make_standard_tomo_loader() -> StandardTomoLoader:
     """
     Create an instance of `StandardTomoLoader` with some commonly used default values for
@@ -654,11 +655,13 @@ def test_standard_tomo_loader_user_defined_angles(
         stop_angle=180,
         angles_total=720,
     )
-    EXPECTED_ANGLES = np.deg2rad(np.linspace(
-        USER_DEFINED_ANGLES.start_angle,
-        USER_DEFINED_ANGLES.stop_angle,
-        USER_DEFINED_ANGLES.angles_total,
-    ))
+    EXPECTED_ANGLES = np.deg2rad(
+        np.linspace(
+            USER_DEFINED_ANGLES.start_angle,
+            USER_DEFINED_ANGLES.stop_angle,
+            USER_DEFINED_ANGLES.angles_total,
+        )
+    )
 
     with mock.patch(
         "httomo.darks_flats.get_darks_flats",
diff --git a/tests/method_wrappers/test_generic.py b/tests/method_wrappers/test_generic.py
index abfe9e8c5..26a9aaa9b 100644
--- a/tests/method_wrappers/test_generic.py
+++ b/tests/method_wrappers/test_generic.py
@@ -24,7 +24,7 @@ def fake_method(data):
         make_mock_repo(mocker, padding=True),
         "testmodule.path",
         "fake_method",
-        MPI.COMM_WORLD
+        MPI.COMM_WORLD,
     )
     assert isinstance(wrp, GenericMethodWrapper)
     assert wrp.method_name == "fake_method"
@@ -46,13 +46,16 @@ def fake_method(data):
         "testmodule.path",
         "fake_method",
         MPI.COMM_WORLD,
-        task_id="fake_method_id"
+        task_id="fake_method_id",
     )
-    
+
     assert wrp.task_id == "fake_method_id"
 
+
 @pytest.mark.cupy
-def test_generic_execute_transfers_to_gpu(mocker: MockerFixture, dummy_block: DataSetBlock):
+def test_generic_execute_transfers_to_gpu(
+    mocker: MockerFixture, dummy_block: DataSetBlock
+):
     class FakeModule:
         def fake_method(data):
             return data
@@ -74,7 +77,8 @@ def fake_method(data):
     reason="skipped as cupy is not available",
 )
 @pytest.mark.cupy
-def test_generic_excute_measures_gpu_times(dummy_block: DataSetBlock, mocker: MockerFixture
+def test_generic_excute_measures_gpu_times(
+    dummy_block: DataSetBlock, mocker: MockerFixture
 ):
     class FakeModule:
         def fake_method(data):
@@ -199,11 +203,9 @@ def fake_method(data, comm: Optional[MPI.Comm] = None):
     wrp.execute(dummy_block)
 
 
-def test_generic_transforms_auto_axis(
-    mocker: MockerFixture, dummy_block: DataSetBlock
-):    
-
+def test_generic_transforms_auto_axis(mocker: MockerFixture, dummy_block: DataSetBlock):
     PATTERN = Pattern.projection
+
     class FakeModule:
         def fake_method(data, axis: int):
             assert axis == PATTERN.value
@@ -211,8 +213,11 @@ def fake_method(data, axis: int):
 
     mocker.patch("importlib.import_module", return_value=FakeModule)
     wrp = make_method_wrapper(
-        make_mock_repo(mocker, pattern=PATTERN), "mocked_module_path", "fake_method",
-        MPI.COMM_WORLD, axis="auto",
+        make_mock_repo(mocker, pattern=PATTERN),
+        "mocked_module_path",
+        "fake_method",
+        MPI.COMM_WORLD,
+        axis="auto",
     )
 
     wrp.execute(dummy_block)
@@ -251,7 +256,9 @@ def fake_method(data, param):
     assert "Cannot map method parameter param to a value" in str(e)
 
 
-def test_generic_access_outputref_params(mocker: MockerFixture, dummy_block: DataSetBlock):
+def test_generic_access_outputref_params(
+    mocker: MockerFixture, dummy_block: DataSetBlock
+):
     class FakeModule:
         def fake_method(data, param):
             assert param == 42
@@ -303,7 +310,9 @@ def fake_method(array):
     wrp.execute(dummy_block)
 
 
-def test_generic_for_method_with_kwargs(mocker: MockerFixture, dummy_block: DataSetBlock):
+def test_generic_for_method_with_kwargs(
+    mocker: MockerFixture, dummy_block: DataSetBlock
+):
     class FakeModule:
         def fake_method(data, param, **kwargs):
             assert param == 42.0
@@ -490,7 +499,7 @@ def test_method(data):
 @pytest.mark.cupy
 def test_generic_calculate_max_slices_direct(
     mocker: MockerFixture,
-  dummy_block: DataSetBlock,
+    dummy_block: DataSetBlock,
     implementation: str,
     memory_gpu: List[GpuMemoryRequirement],
 ):
@@ -517,7 +526,11 @@ def test_method(data):
     shape = (shape_t[0], shape_t[1])
     databytes = shape[0] * shape[1] * dummy_block.data.itemsize
     max_slices_expected = 5
-    multiplier = float(memory_gpu[0].multiplier if memory_gpu != [] and memory_gpu[0].multiplier is not None else 1)
+    multiplier = float(
+        memory_gpu[0].multiplier
+        if memory_gpu != [] and memory_gpu[0].multiplier is not None
+        else 1
+    )
     available_memory_in = int(databytes * max_slices_expected * multiplier)
     if available_memory_in == 0:
         available_memory_in = 5
@@ -660,7 +673,9 @@ def test_generic_execute_uses_comm_passed_to_constructor(
     # The fact that its `bcast()` method should never be called is used as a proxy/marker that
     # the global communicator is never used in method execution, only the communicator passed
     # into the method wrapper's constructor should be used in method execution.
-    global_comm_bcast_spy = mocker.patch.object(target=global_comm_mock, attribute="bcast")
+    global_comm_bcast_spy = mocker.patch.object(
+        target=global_comm_mock, attribute="bcast"
+    )
 
     # Define mock comm object to pass into method wrapper constructor
     passed_in_mock_comm = mocker.create_autospec(MPI.Comm)
diff --git a/tests/method_wrappers/test_reconstruction.py b/tests/method_wrappers/test_reconstruction.py
index 5004cc7c3..343c3f5e8 100644
--- a/tests/method_wrappers/test_reconstruction.py
+++ b/tests/method_wrappers/test_reconstruction.py
@@ -14,6 +14,7 @@
 
 def test_recon_handles_reconstruction_angle_reshape(mocker: MockerFixture):
     GLOBAL_SHAPE = (10, 20, 30)
+
     class FakeModule:
         # we give the angles a different name on purpose
         def recon_tester(data, theta):
@@ -30,14 +31,21 @@ def recon_tester(data, theta):
         MPI.COMM_WORLD,
     )
     assert isinstance(wrp, ReconstructionWrapper)
-    
-    
-    aux_data = AuxiliaryData(angles=2.*np.ones(GLOBAL_SHAPE[0]+10, dtype=np.float32))
+
+    aux_data = AuxiliaryData(
+        angles=2.0 * np.ones(GLOBAL_SHAPE[0] + 10, dtype=np.float32)
+    )
     data = np.ones(GLOBAL_SHAPE, dtype=np.float32)
-    input = DataSetBlock(data[:, 0:3, :], slicing_dim=1, aux_data=aux_data, chunk_shape=GLOBAL_SHAPE, global_shape=GLOBAL_SHAPE)
-    
+    input = DataSetBlock(
+        data[:, 0:3, :],
+        slicing_dim=1,
+        aux_data=aux_data,
+        chunk_shape=GLOBAL_SHAPE,
+        global_shape=GLOBAL_SHAPE,
+    )
+
     wrp.execute(input)
-    
+
     assert aux_data.get_angles().shape[0] == GLOBAL_SHAPE[0]
 
 
@@ -53,14 +61,16 @@ def recon_tester(data, theta):
         "recon_tester",
         MPI.COMM_WORLD,
     )
-    block = DataSetBlock(data=np.ones((13, 14, 15), dtype=np.float32),
+    block = DataSetBlock(
+        data=np.ones((13, 14, 15), dtype=np.float32),
         aux_data=AuxiliaryData(angles=np.ones(13, dtype=np.float32)),
-        slicing_dim=1
+        slicing_dim=1,
     )
     res = wrp.execute(block)
 
     assert res.data.shape == (13, 14, 15)
 
+
 def test_recon_changes_global_shape_if_size_changes(mocker: MockerFixture):
     class FakeModule:
         def recon_tester(data, theta):
@@ -74,14 +84,15 @@ def recon_tester(data, theta):
         "recon_tester",
         MPI.COMM_WORLD,
     )
-    block = DataSetBlock(data=np.ones((13, 3, 15), dtype=np.float32),
+    block = DataSetBlock(
+        data=np.ones((13, 3, 15), dtype=np.float32),
         aux_data=AuxiliaryData(angles=np.ones(13, dtype=np.float32)),
         slicing_dim=1,
         global_shape=(13, 14, 15),
-        chunk_shape=(13, 14, 15)
+        chunk_shape=(13, 14, 15),
     )
     res = wrp.execute(block)
-    
+
     assert res.shape == (30, 3, 15)
     assert res.global_shape == (30, 14, 15)
     assert res.chunk_shape == (30, 14, 15)
@@ -120,4 +131,4 @@ def tester(data, algorithm):
 
     assert wrp.recon_algorithm is None
     wrp["algorithm"] = "testalgo"
-    assert wrp.recon_algorithm is None
\ No newline at end of file
+    assert wrp.recon_algorithm is None
diff --git a/tests/method_wrappers/test_rotation.py b/tests/method_wrappers/test_rotation.py
index ac77d9067..8c1a2aa82 100644
--- a/tests/method_wrappers/test_rotation.py
+++ b/tests/method_wrappers/test_rotation.py
@@ -217,7 +217,6 @@ def test_rotation_normalize_sino_no_darks_flats():
 
 
 def test_rotation_normalize_sino_same_darks_flats():
-
     self_mock = MagicMock()  # this mocks self - as function only sets gpu time
     ret = RotationWrapper.normalize_sino(
         self_mock,
diff --git a/tests/method_wrappers/test_stats_calc.py b/tests/method_wrappers/test_stats_calc.py
index 81bd9292d..b58b56bb0 100644
--- a/tests/method_wrappers/test_stats_calc.py
+++ b/tests/method_wrappers/test_stats_calc.py
@@ -9,7 +9,6 @@
 from httomo.utils import gpu_enabled, xp
 
 
-
 def test_calculate_stats(mocker: MockerFixture, dummy_block: DataSetBlock):
     class FakeModule:
         def calculate_stats(data, comm):
@@ -113,7 +112,6 @@ def calculate_stats(data, comm):
 def test_calculate_stats_uses_gpu_if_available(
     mocker: MockerFixture, dummy_block: DataSetBlock, gpu: bool
 ):
-
     if gpu and not gpu_enabled:
         pytest.skip("No GPU available")
 
diff --git a/tests/monitors/test_aggregate.py b/tests/monitors/test_aggregate.py
index f25688e73..4c51f2c22 100644
--- a/tests/monitors/test_aggregate.py
+++ b/tests/monitors/test_aggregate.py
@@ -14,8 +14,19 @@ def test_aggregate_passes_on_method(mocker: MockerFixture):
     mon1 = mocker.create_autospec(MonitoringInterface, instance=True)
     mon2 = mocker.create_autospec(MonitoringInterface, instance=True)
     agg = AggregateMonitoring([mon1, mon2])
-    args = ("method", "module", "task", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 
-            42.0, 3.2, 1.2, 1.1)
+    args = (
+        "method",
+        "module",
+        "task",
+        0,
+        (1, 2, 3),
+        (0, 0, 0),
+        (10, 0, 0),
+        42.0,
+        3.2,
+        1.2,
+        1.1,
+    )
     agg.report_method_block(*args)
 
     mon1.report_method_block.assert_called_once_with(*args)
diff --git a/tests/monitors/test_benchmark.py b/tests/monitors/test_benchmark.py
index 1e42d551a..59bf8ced5 100644
--- a/tests/monitors/test_benchmark.py
+++ b/tests/monitors/test_benchmark.py
@@ -8,10 +8,30 @@
 def test_benchmark_monitor_records_and_displays_data():
     mon = BenchmarkMonitoring(MPI.COMM_WORLD)
     mon.report_method_block(
-        "method1", "module", "task", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 42.0, 2.0, 0.1, 0.2
+        "method1",
+        "module",
+        "task",
+        0,
+        (1, 2, 3),
+        (0, 0, 0),
+        (10, 0, 0),
+        42.0,
+        2.0,
+        0.1,
+        0.2,
     )
     mon.report_method_block(
-        "method2", "module", "task", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 42.0, 2.0, 0.1, 0.2
+        "method2",
+        "module",
+        "task",
+        0,
+        (1, 2, 3),
+        (0, 0, 0),
+        (10, 0, 0),
+        42.0,
+        2.0,
+        0.1,
+        0.2,
     )
     mon.report_source_block(
         "loader", "method1", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 4.0
@@ -28,22 +48,25 @@ def test_benchmark_monitor_records_and_displays_data():
     dest.flush()
     data = dest.getvalue().splitlines()
     assert len(data) == 8
-    assert data[0] == ",".join([
-        "Type",
-        "Rank","Name",
-        "Task id",
-        "Module",
-        "Slicing dim",
-        "Block offset (chunk)",
-        "Block offset (global)",
-        "Block dim z",
-        "Block dim y",
-        "Block dim x",
-        "CPU time",
-        "GPU kernel time",
-        "GPU H2D time",
-        "GPU D2H time"
-    ])
+    assert data[0] == ",".join(
+        [
+            "Type",
+            "Rank",
+            "Name",
+            "Task id",
+            "Module",
+            "Slicing dim",
+            "Block offset (chunk)",
+            "Block offset (global)",
+            "Block dim z",
+            "Block dim y",
+            "Block dim x",
+            "CPU time",
+            "GPU kernel time",
+            "GPU H2D time",
+            "GPU D2H time",
+        ]
+    )
 
     assert "42.0,2.0,0.1,0.2" in data[1]
     assert "42.0,2.0,0.1,0.2" in data[2]
@@ -64,7 +87,17 @@ def test_summary_monitor_records_and_displays_data_mpi():
     # everything gets reported twice - once in each process - and the write_results should aggregate
     # in process 0
     mon.report_method_block(
-        "method1", "module", "task", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 42.0, 2.0, 0.1, 0.2
+        "method1",
+        "module",
+        "task",
+        0,
+        (1, 2, 3),
+        (0, 0, 0),
+        (10, 0, 0),
+        42.0,
+        2.0,
+        0.1,
+        0.2,
     )
     mon.report_source_block(
         "loader", "method1", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 4.0
@@ -80,29 +113,32 @@ def test_summary_monitor_records_and_displays_data_mpi():
         assert len(data) == 0
     else:
         assert len(data) == 9
-        assert data[0] == ",".join([
-            "Type",
-            "Rank","Name",
-            "Task id",
-            "Module",
-            "Slicing dim",
-            "Block offset (chunk)",
-            "Block offset (global)",
-            "Block dim z",
-            "Block dim y",
-            "Block dim x",
-            "CPU time",
-            "GPU kernel time",
-            "GPU H2D time",
-            "GPU D2H time"
-        ])
+        assert data[0] == ",".join(
+            [
+                "Type",
+                "Rank",
+                "Name",
+                "Task id",
+                "Module",
+                "Slicing dim",
+                "Block offset (chunk)",
+                "Block offset (global)",
+                "Block dim z",
+                "Block dim y",
+                "Block dim x",
+                "CPU time",
+                "GPU kernel time",
+                "GPU H2D time",
+                "GPU D2H time",
+            ]
+        )
 
         for rank in [0, 1]:
-            assert "42.0,2.0,0.1,0.2" in data[1+rank*4]
-            assert f"method,{rank},method1" in data[1+rank*4]
-            assert "4.0,0.0" in data[2+rank*4]
-            assert f"source,{rank},loader" in data[2+rank*4]
-            assert "3.0,0.0" in data[3+rank*4]
-            assert f"sink,{rank},wrt1" in data[3+rank*4]
-            assert "500.0,0.0" in data[4+rank*4]
-            assert f"total,{rank},," in data[4+rank*4]
+            assert "42.0,2.0,0.1,0.2" in data[1 + rank * 4]
+            assert f"method,{rank},method1" in data[1 + rank * 4]
+            assert "4.0,0.0" in data[2 + rank * 4]
+            assert f"source,{rank},loader" in data[2 + rank * 4]
+            assert "3.0,0.0" in data[3 + rank * 4]
+            assert f"sink,{rank},wrt1" in data[3 + rank * 4]
+            assert "500.0,0.0" in data[4 + rank * 4]
+            assert f"total,{rank},," in data[4 + rank * 4]
diff --git a/tests/monitors/test_summary.py b/tests/monitors/test_summary.py
index 1f78044cd..9efd1fd20 100644
--- a/tests/monitors/test_summary.py
+++ b/tests/monitors/test_summary.py
@@ -27,7 +27,7 @@ def test_summary_monitor_records_and_displays_data():
     mon.write_results(dest)
     dest.flush()
     data = dest.getvalue()
-    
+
     assert "across 1 process" in data
     assert "Summary Statistics" in data
     assert "methods CPU time" in data
@@ -45,21 +45,41 @@ def test_summary_monitor_records_and_displays_data():
     assert "method1" in data
     assert "method2" in data
 
+
 @pytest.mark.mpi
 @pytest.mark.skipif(
     MPI.COMM_WORLD.size != 2, reason="Only rank-2 MPI is supported with this test"
 )
 def test_summary_monitor_records_and_displays_data_mpi():
-
     comm = MPI.COMM_WORLD
     mon = SummaryMonitor(comm)
     # everything gets reported twice - once in each process - and the write_results should aggregate
     # in process 0
     mon.report_method_block(
-        "method1", "module", "task", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 42.0, 2.0, 0.1, 0.2
+        "method1",
+        "module",
+        "task",
+        0,
+        (1, 2, 3),
+        (0, 0, 0),
+        (10, 0, 0),
+        42.0,
+        2.0,
+        0.1,
+        0.2,
     )
     mon.report_method_block(
-        "method2", "module", "task", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 42.0, 2.0, 0.1, 0.2
+        "method2",
+        "module",
+        "task",
+        0,
+        (1, 2, 3),
+        (0, 0, 0),
+        (10, 0, 0),
+        42.0,
+        2.0,
+        0.1,
+        0.2,
     )
     mon.report_source_block(
         "loader", "method1", 0, (1, 2, 3), (0, 0, 0), (10, 0, 0), 4.0
diff --git a/tests/runner/test_block_split.py b/tests/runner/test_block_split.py
index 1bc8c23b2..0b78f0711 100644
--- a/tests/runner/test_block_split.py
+++ b/tests/runner/test_block_split.py
@@ -133,4 +133,3 @@ def test_block_can_iterate(mocker: MockerFixture, slicing_dim: int):
     source.read_block.assert_has_calls(
         [call(0, max_slices), call(max_slices, max_slices)]
     )
-
diff --git a/tests/runner/test_dataset_store_interfaces.py b/tests/runner/test_dataset_store_interfaces.py
index 63491f4b4..d4fbef939 100644
--- a/tests/runner/test_dataset_store_interfaces.py
+++ b/tests/runner/test_dataset_store_interfaces.py
@@ -5,8 +5,7 @@
 def test_dummy_dataset_sink(dummy_block: DataSetBlock):
     dummy_sink = DummySink(0)
     dummy_sink.write_block(dummy_block)
-    
+
     assert dummy_sink.global_shape == dummy_block.global_shape
     assert dummy_sink.global_index == dummy_block.chunk_index
     assert dummy_sink.chunk_shape == dummy_block.chunk_shape
-    
\ No newline at end of file
diff --git a/tests/runner/test_gpu_utils.py b/tests/runner/test_gpu_utils.py
index a4ef0496a..780c83a19 100644
--- a/tests/runner/test_gpu_utils.py
+++ b/tests/runner/test_gpu_utils.py
@@ -17,7 +17,6 @@ def test_get_available_memory():
     assert mem <= 0.9 * xp.cuda.Device(get_gpu_id()).mem_info[0]
 
 
-
 def test_get_available_memory_cpu(mocker: MockerFixture):
     if gpu_enabled:
         # this function is called in the implementation try block -
diff --git a/tests/runner/test_pipeline.py b/tests/runner/test_pipeline.py
index ae3bd3c8a..75ef9e331 100644
--- a/tests/runner/test_pipeline.py
+++ b/tests/runner/test_pipeline.py
@@ -45,6 +45,6 @@ def test_pipeline_can_access_by_index(mocker: MockerFixture):
             make_test_method(mocker, method_name="m3"),
         ],
     )
-    
+
     for i in range(3):
         assert p[i].method_name == f"m{i+1}"
diff --git a/tests/runner/test_section.py b/tests/runner/test_section.py
index 6169e4de0..5797c16b8 100644
--- a/tests/runner/test_section.py
+++ b/tests/runner/test_section.py
@@ -253,10 +253,12 @@ def test_sectionizer_output_ref_after_regular_section_break_does_nothing(
     assert len(s[0]) == 6
     assert len(s[1]) == 6
 
-@pytest.mark.parametrize("patterns", [
-    (Pattern.sinogram, Pattern.projection),
-    (Pattern.projection, Pattern.sinogram)
-], ids=["sino-proj", "proj-sino"])
+
+@pytest.mark.parametrize(
+    "patterns",
+    [(Pattern.sinogram, Pattern.projection), (Pattern.projection, Pattern.sinogram)],
+    ids=["sino-proj", "proj-sino"],
+)
 def test_sectionizer_inserts_empty_section_if_loader_pattern_mismatches(
     mocker: MockerFixture, patterns: Tuple[Pattern, Pattern]
 ):
@@ -274,6 +276,7 @@ def test_sectionizer_inserts_empty_section_if_loader_pattern_mismatches(
     assert len(s[1]) == 1
     assert s[1].pattern == patterns[1]
 
+
 @pytest.mark.parametrize("padding", [False, True])
 def test_sectionizer_sets_padding_property(mocker: MockerFixture, padding: bool):
     p = Pipeline(
@@ -283,20 +286,25 @@ def test_sectionizer_sets_padding_property(mocker: MockerFixture, padding: bool)
     s = sectionize(p)
 
     assert s[-1].padding is padding
-    
+
+
 def test_sectionizer_splits_section_if_multiple_padding_methods(mocker: MockerFixture):
     p = Pipeline(
         loader=make_test_loader(mocker, pattern=Pattern.projection),
-        methods=[make_test_method(mocker, pattern=Pattern.projection, padding=False),
-                 make_test_method(mocker, pattern=Pattern.projection, padding=True),
-                 make_test_method(mocker, pattern=Pattern.projection, padding=False),
-                 make_test_method(mocker, pattern=Pattern.projection, padding=True)],
+        methods=[
+            make_test_method(mocker, pattern=Pattern.projection, padding=False),
+            make_test_method(mocker, pattern=Pattern.projection, padding=True),
+            make_test_method(mocker, pattern=Pattern.projection, padding=False),
+            make_test_method(mocker, pattern=Pattern.projection, padding=True),
+        ],
     )
-    
+
     s = sectionize(p)
-    
+
     assert len(s) == 2
     assert s[0].padding is True
     assert s[1].padding is True
-    assert len(s[0]) == 3  # loader + 3 methods, as we want right before the next padding method
-    assert len(s[1]) == 1  # just the last method with padding
\ No newline at end of file
+    assert (
+        len(s[0]) == 3
+    )  # loader + 3 methods, as we want right before the next padding method
+    assert len(s[1]) == 1  # just the last method with padding
diff --git a/tests/samples/pipeline_template_examples/DLS/01_diad_pipeline_gpu.yaml b/tests/samples/pipeline_template_examples/DLS/01_diad_pipeline_gpu.yaml
index 2b50021b9..2be755b7e 100644
--- a/tests/samples/pipeline_template_examples/DLS/01_diad_pipeline_gpu.yaml
+++ b/tests/samples/pipeline_template_examples/DLS/01_diad_pipeline_gpu.yaml
@@ -56,7 +56,7 @@
   parameters: {}
   id: statistics
   side_outputs:
-    glob_stats: glob_stats  
+    glob_stats: glob_stats
 - method: save_to_images
   module_path: httomolib.misc.images
   parameters:
diff --git a/tests/samples/pipeline_template_examples/pipeline_360deg_gpu2.yaml b/tests/samples/pipeline_template_examples/pipeline_360deg_gpu2.yaml
index daf20d34c..303689d22 100644
--- a/tests/samples/pipeline_template_examples/pipeline_360deg_gpu2.yaml
+++ b/tests/samples/pipeline_template_examples/pipeline_360deg_gpu2.yaml
@@ -43,7 +43,7 @@
   module_path: httomolibgpu.misc.morph
   parameters:
     overlap: ${{centering.side_outputs.overlap}}
-    rotation: right    
+    rotation: right
 - method: FBP
   module_path: httomolibgpu.recon.algorithm
   parameters:
@@ -57,7 +57,7 @@
   parameters: {}
   id: statistics
   side_outputs:
-    glob_stats: glob_stats  
+    glob_stats: glob_stats
 - method: save_to_images
   module_path: httomolib.misc.images
   parameters:
@@ -68,4 +68,4 @@
     perc_range_min: 0.0
     perc_range_max: 95.0
     jpeg_quality: 95
-    glob_stats: ${{statistics.side_outputs.glob_stats}}    
+    glob_stats: ${{statistics.side_outputs.glob_stats}}
diff --git a/tests/samples/pipeline_template_examples/pipeline_360deg_iterative_gpu3.yaml b/tests/samples/pipeline_template_examples/pipeline_360deg_iterative_gpu3.yaml
index 4a548d5e7..6978b5730 100644
--- a/tests/samples/pipeline_template_examples/pipeline_360deg_iterative_gpu3.yaml
+++ b/tests/samples/pipeline_template_examples/pipeline_360deg_iterative_gpu3.yaml
@@ -60,13 +60,13 @@
   parameters:
     newshape: [256, 256]
     axis: auto
-    interpolation: nearest  
+    interpolation: nearest
 - method: calculate_stats
   module_path: httomo.methods
   parameters: {}
   id: statistics
   side_outputs:
-    glob_stats: glob_stats  
+    glob_stats: glob_stats
 - method: save_to_images
   module_path: httomolib.misc.images
   parameters:
diff --git a/tests/samples/pipeline_template_examples/pipeline_cpu1.yaml b/tests/samples/pipeline_template_examples/pipeline_cpu1.yaml
index 8527d0f26..3f4d2bbbb 100644
--- a/tests/samples/pipeline_template_examples/pipeline_cpu1.yaml
+++ b/tests/samples/pipeline_template_examples/pipeline_cpu1.yaml
@@ -1,6 +1,6 @@
 - method: standard_tomo
   module_path: httomo.data.hdf.loaders
-  parameters: 
+  parameters:
     name: tomo
     data_path: entry1/tomo_entry/data/data
     image_key_path: entry1/tomo_entry/instrument/detector/image_key
diff --git a/tests/samples/pipeline_template_examples/pipeline_cpu3.yaml b/tests/samples/pipeline_template_examples/pipeline_cpu3.yaml
index af55dc3d1..3ef574596 100644
--- a/tests/samples/pipeline_template_examples/pipeline_cpu3.yaml
+++ b/tests/samples/pipeline_template_examples/pipeline_cpu3.yaml
@@ -58,4 +58,4 @@
     perc_range_max: 100.0
     jpeg_quality: 95
     glob_stats: ${{statistics.side_outputs.glob_stats}}
-    
+
diff --git a/tests/samples/pipeline_template_examples/pipeline_cpu4.yaml b/tests/samples/pipeline_template_examples/pipeline_cpu4.yaml
index e8e2eb8be..4347eadec 100644
--- a/tests/samples/pipeline_template_examples/pipeline_cpu4.yaml
+++ b/tests/samples/pipeline_template_examples/pipeline_cpu4.yaml
@@ -1,6 +1,6 @@
 - method: standard_tomo
   module_path: httomo.data.hdf.loaders
-  parameters: 
+  parameters:
     name: tomo
     data_path: entry1/tomo_entry/data/data
     image_key_path: entry1/tomo_entry/instrument/detector/image_key
diff --git a/tests/samples/pipeline_template_examples/testing/invalid_reference.yaml b/tests/samples/pipeline_template_examples/testing/invalid_reference.yaml
index 959edca36..1060f2c9c 100644
--- a/tests/samples/pipeline_template_examples/testing/invalid_reference.yaml
+++ b/tests/samples/pipeline_template_examples/testing/invalid_reference.yaml
@@ -28,7 +28,7 @@
     drop: 20
   id: centering
   side_outputs:
-    cor: centre_of_rotation  
+    cor: centre_of_rotation
 - method: remove_stripe_fw
   module_path: tomopy.prep.stripe
   parameters:
diff --git a/tests/samples/pipeline_template_examples/testing/testing_pipeline.yaml b/tests/samples/pipeline_template_examples/testing/testing_pipeline.yaml
index 6577495f9..55025f702 100644
--- a/tests/samples/pipeline_template_examples/testing/testing_pipeline.yaml
+++ b/tests/samples/pipeline_template_examples/testing/testing_pipeline.yaml
@@ -18,7 +18,7 @@
     drop: 20
   id: centering
   side_outputs:
-    cor: centre_of_rotation  
+    cor: centre_of_rotation
 - method: remove_stripe_fw
   module_path: tomopy.prep.stripe
   parameters:
diff --git a/tests/samples/python_templates/pipeline_cpu1.py b/tests/samples/python_templates/pipeline_cpu1.py
index 980619262..22be20ba1 100644
--- a/tests/samples/python_templates/pipeline_cpu1.py
+++ b/tests/samples/python_templates/pipeline_cpu1.py
@@ -5,9 +5,10 @@
 PipelineConfig: TypeAlias = List[PipelineStageConfig]
 
 # NOTE: when creating a Pythonic pipeline, please use
-# the function's name "methods_to_list" so it will be 
+# the function's name "methods_to_list" so it will be
 # found by the loader
 
+
 def methods_to_list() -> PipelineConfig:
     """Pythonic way to build a list of tasks
     from which Pipeline can be generated in Httomo.
@@ -18,73 +19,72 @@ def methods_to_list() -> PipelineConfig:
     """
     full_pipeline_list = []
     loader = {
-        'method': "standard_tomo",
-        'module_path': "httomo.data.hdf.loaders",
-        'parameters' : {
-                     'name': 'tomo',
-                     'data_path': 'entry1/tomo_entry/data/data',
-                     'image_key_path': 'entry1/tomo_entry/instrument/detector/image_key',
-                     'rotation_angles': {"data_path": "/entry1/tomo_entry/data/rotation_angle"},
-                     'dimension': 1,
-                     'pad': 0,
-                       },
-                    }
+        "method": "standard_tomo",
+        "module_path": "httomo.data.hdf.loaders",
+        "parameters": {
+            "name": "tomo",
+            "data_path": "entry1/tomo_entry/data/data",
+            "image_key_path": "entry1/tomo_entry/instrument/detector/image_key",
+            "rotation_angles": {"data_path": "/entry1/tomo_entry/data/rotation_angle"},
+            "dimension": 1,
+            "pad": 0,
+        },
+    }
     full_pipeline_list.append(loader)
     method1 = {
-        'method': "normalize",
-        'module_path': "tomopy.prep.normalize",
-        'parameters' : {
-                     'cutoff': None,
-                       },
-                    }
+        "method": "normalize",
+        "module_path": "tomopy.prep.normalize",
+        "parameters": {
+            "cutoff": None,
+        },
+    }
     full_pipeline_list.append(method1)
     method2 = {
-        'method': "minus_log",
-        'module_path': "tomopy.prep.normalize",
-        'parameters' : {},
-                    }
+        "method": "minus_log",
+        "module_path": "tomopy.prep.normalize",
+        "parameters": {},
+    }
     full_pipeline_list.append(method2)
     method3 = {
-        'method': "find_center_vo",
-        'module_path': "tomopy.recon.rotation",
-        'id': "centering",
-        'parameters' : {
-                     'ind': "mid",
-                     'smin': -50,
-                     'smax': 50,
-                     'srad': 6,
-                     'step': 0.25,
-                     'ratio': 0.5,
-                     'drop': 20,
-                       },
-        'side_outputs': {"cor": "centre_of_rotation"},
-                    }
+        "method": "find_center_vo",
+        "module_path": "tomopy.recon.rotation",
+        "id": "centering",
+        "parameters": {
+            "ind": "mid",
+            "smin": -50,
+            "smax": 50,
+            "srad": 6,
+            "step": 0.25,
+            "ratio": 0.5,
+            "drop": 20,
+        },
+        "side_outputs": {"cor": "centre_of_rotation"},
+    }
     full_pipeline_list.append(method3)
     method4 = {
-        'method': "recon",
-        'module_path': "tomopy.recon.algorithm",
-        'parameters' : {
-                     'center': "${{centering.side_outputs.centre_of_rotation}}",
-                     'sinogram_order': False,
-                     'algorithm': "gridrec",
-                     'init_recon': None,
-                       },
-                    }
+        "method": "recon",
+        "module_path": "tomopy.recon.algorithm",
+        "parameters": {
+            "center": "${{centering.side_outputs.centre_of_rotation}}",
+            "sinogram_order": False,
+            "algorithm": "gridrec",
+            "init_recon": None,
+        },
+    }
     full_pipeline_list.append(method4)
     method5 = {
-        'method': "save_to_images",
-        'module_path': "httomolib.misc.images",
-        'parameters' : {
-                     'subfolder_name': "images",
-                     'axis': 1,
-                     'file_format': "tif",
-                     'bits': 8,
-                     'perc_range_min': 0.0,
-                     'perc_range_max': 100.0,
-                     'jpeg_quality': 95,
-                       },
-                    }
+        "method": "save_to_images",
+        "module_path": "httomolib.misc.images",
+        "parameters": {
+            "subfolder_name": "images",
+            "axis": 1,
+            "file_format": "tif",
+            "bits": 8,
+            "perc_range_min": 0.0,
+            "perc_range_max": 100.0,
+            "jpeg_quality": 95,
+        },
+    }
     full_pipeline_list.append(method5)
 
     return full_pipeline_list
-    
diff --git a/tests/test_backends/test_httomolibgpu.py b/tests/test_backends/test_httomolibgpu.py
index e7f81a51a..70fa8ecfb 100644
--- a/tests/test_backends/test_httomolibgpu.py
+++ b/tests/test_backends/test_httomolibgpu.py
@@ -43,7 +43,6 @@
 
 
 class MaxMemoryHook(cp.cuda.MemoryHook):
-
     def __init__(self, initial=0):
         self.max_mem = initial
         self.current = initial
@@ -467,7 +466,7 @@ def test_recon_FBP_memoryhook(
     # the estimated_memory_mb should be LARGER or EQUAL to max_mem_mb
     # the resulting percent value should not deviate from max_mem on more than 20%
     assert estimated_memory_mb >= max_mem_mb
-    assert percents_relative_maxmem <= 150 # big underestimation, to be looked into
+    assert percents_relative_maxmem <= 150  # big underestimation, to be looked into
 
 
 @pytest.mark.cupy
diff --git a/tests/test_backends/test_tomopy.py b/tests/test_backends/test_tomopy.py
index e394b19d7..cab71a8e8 100644
--- a/tests/test_backends/test_tomopy.py
+++ b/tests/test_backends/test_tomopy.py
@@ -29,8 +29,10 @@ def test_calc_padding_remove_outlier3d_defaults() -> None:
     kwargs: dict = {}
     assert _calc_padding_remove_outlier3d(**kwargs) == (1, 1)
 
+
 # TODO: unclear if the following function's padding behaviour is
-# doing the right thing... 
+# doing the right thing...
+
 
 def test_calc_padding_stripes_detect3d() -> None:
     kwargs: dict = {"size": 15, "radius": 2, "ncore": 10}
@@ -44,7 +46,7 @@ def test_calc_padding_stripes_detect3d_defaults() -> None:
 
 
 def test_calc_padding_stripes_mask3d() -> None:
-    kwargs: dict = {"threshold": .7, "min_stripe_depth": 5}
+    kwargs: dict = {"threshold": 0.7, "min_stripe_depth": 5}
     assert _calc_padding_stripes_mask3d(**kwargs) == (5, 5)
 
 
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 6cfc13000..462fa4414 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -55,26 +55,27 @@ def test_cli_pass_gpu_id(cmd, standard_data, standard_loader, output_folder):
     assert "GPU Device not available for access." in result.stderr
 
 
-@pytest.mark.parametrize("cli_parameter,limit_bytes", [
-    ("0", 0),
-    ("500", 500),
-    ("500k", 500 * 1024),
-    ("1M",  1024*1024),
-    ("1m",  1024*1024),
-    ("3g", 3 * 1024**3),
-    ("3.2g", int(3.2 * 1024**3))
-])
+@pytest.mark.parametrize(
+    "cli_parameter,limit_bytes",
+    [
+        ("0", 0),
+        ("500", 500),
+        ("500k", 500 * 1024),
+        ("1M", 1024 * 1024),
+        ("1m", 1024 * 1024),
+        ("3g", 3 * 1024**3),
+        ("3.2g", int(3.2 * 1024**3)),
+    ],
+)
 def test_cli_transforms_memory_limits(cli_parameter: str, limit_bytes: int):
     assert transform_limit_str_to_bytes(cli_parameter) == limit_bytes
-    
 
-@pytest.mark.parametrize("cli_parameter", [
-    "abcd", "nolimit", "124A", "23ki"
-])
+
+@pytest.mark.parametrize("cli_parameter", ["abcd", "nolimit", "124A", "23ki"])
 def test_cli_fails_transforming_memory_limits(cli_parameter: str):
     with pytest.raises(ValueError) as e:
         transform_limit_str_to_bytes(cli_parameter)
-        
+
     assert f"invalid memory limit string {cli_parameter}" in str(e)
 
 
diff --git a/tests/test_cli_utils.py b/tests/test_cli_utils.py
index a26824a08..4376309d3 100644
--- a/tests/test_cli_utils.py
+++ b/tests/test_cli_utils.py
@@ -11,7 +11,7 @@
         ("samples/pipeline_template_examples/testing/example.yaml", False),
         ("samples/pipeline_template_examples/testing/sweep_manual.py", True),
         ("samples/python_templates/pipeline_cpu1.py", False),
-    ]
+    ],
 )
 def test_is_sweep_pipeline(pipeline_file: Path, expected_is_sweep_pipeline: bool):
     pipeline_file_path = Path(__file__).parent / pipeline_file
diff --git a/tests/test_methods.py b/tests/test_methods.py
index 7e3687e23..67612b3a4 100644
--- a/tests/test_methods.py
+++ b/tests/test_methods.py
@@ -40,18 +40,18 @@ def test_calculate_stats_gpu():
     ret = calculate_stats(data)
 
     assert ret == (-10.0, 19.0, np.sum(data.get()), 30)
-    
+
 
 @pytest.mark.perf
 @pytest.mark.parametrize("gpu", [False, True], ids=["CPU", "GPU"])
 def test_calculcate_stats_performance(gpu: bool):
     if gpu and not gpu_enabled:
         pytest.skip("No GPU available")
-        
+
     data = np.random.randint(
         low=7515, high=37624, size=(1801, 5, 2560), dtype=np.uint32
     ).astype(np.float32)
-    
+
     if gpu:
         data = xp.asarray(data)
         xp.cuda.Device().synchronize()
@@ -61,32 +61,38 @@ def test_calculcate_stats_performance(gpu: bool):
     if gpu:
         xp.cuda.Device().synchronize()
     stop = time.perf_counter_ns()
-    duration_ms = float(stop-start) * 1e-6 / 10
-    
-    # Note: on Quadro RTX 6000 vs Xeon(R) Gold 6148, GPU is about 10x faster 
+    duration_ms = float(stop - start) * 1e-6 / 10
+
+    # Note: on Quadro RTX 6000 vs Xeon(R) Gold 6148, GPU is about 10x faster
     assert "performance in ms" == duration_ms
-    
-    
+
+
 def test_save_intermediate_data(tmp_path: Path):
     # use increasing numbers in the data, to make sure blocks have different content
-    GLOBAL_SHAPE=(10,10,10)
+    GLOBAL_SHAPE = (10, 10, 10)
     global_data = np.arange(np.prod(GLOBAL_SHAPE), dtype=np.float32).reshape(
         GLOBAL_SHAPE
     )
     aux_data = AuxiliaryData(angles=np.ones(GLOBAL_SHAPE[0], dtype=np.float32))
     bsize = 3
-    b1 = DataSetBlock(data=global_data[:bsize], aux_data=aux_data,
-                      slicing_dim=0,
-                      block_start=0,
-                      chunk_start=0,
-                      chunk_shape=GLOBAL_SHAPE,
-                      global_shape=GLOBAL_SHAPE)
-    b2 = DataSetBlock(data=global_data[bsize:], aux_data=aux_data,
-                      slicing_dim=0,
-                      block_start=bsize,
-                      chunk_start=0,
-                      chunk_shape=GLOBAL_SHAPE,
-                      global_shape=GLOBAL_SHAPE)
+    b1 = DataSetBlock(
+        data=global_data[:bsize],
+        aux_data=aux_data,
+        slicing_dim=0,
+        block_start=0,
+        chunk_start=0,
+        chunk_shape=GLOBAL_SHAPE,
+        global_shape=GLOBAL_SHAPE,
+    )
+    b2 = DataSetBlock(
+        data=global_data[bsize:],
+        aux_data=aux_data,
+        slicing_dim=0,
+        block_start=bsize,
+        chunk_start=0,
+        chunk_shape=GLOBAL_SHAPE,
+        global_shape=GLOBAL_SHAPE,
+    )
 
     with h5py.File(
         tmp_path / "test_file.h5", "w", driver="mpio", comm=MPI.COMM_WORLD
@@ -139,14 +145,16 @@ def test_save_intermediate_data_mpi(tmp_path: Path):
     # make sure we use the same tmp_path on both processes
     tmp_path = comm.bcast(tmp_path)
     GLOBAL_SHAPE = (10, 10, 10)
-    csize=5
+    csize = 5
     # use increasing numbers in the data, to make sure blocks have different content
     global_data = np.arange(np.prod(GLOBAL_SHAPE), dtype=np.float32).reshape(
         GLOBAL_SHAPE
     )
     aux_data = AuxiliaryData(angles=np.ones(GLOBAL_SHAPE[0], dtype=np.float32))
     # give each process only a portion of the data
-    rank_data = global_data[:csize, :, :] if comm.rank == 0 else global_data[csize:, :, :]
+    rank_data = (
+        global_data[:csize, :, :] if comm.rank == 0 else global_data[csize:, :, :]
+    )
     # create 2 blocks per rank
     b1 = DataSetBlock(
         data=rank_data[:3, :, :],
@@ -155,7 +163,7 @@ def test_save_intermediate_data_mpi(tmp_path: Path):
         block_start=0,
         chunk_start=0 if comm.rank == 0 else csize,
         global_shape=GLOBAL_SHAPE,
-        chunk_shape=(csize, GLOBAL_SHAPE[1], GLOBAL_SHAPE[2])
+        chunk_shape=(csize, GLOBAL_SHAPE[1], GLOBAL_SHAPE[2]),
     )
     b2 = DataSetBlock(
         data=rank_data[3:, :, :],
@@ -164,7 +172,7 @@ def test_save_intermediate_data_mpi(tmp_path: Path):
         block_start=3,
         chunk_start=0 if comm.rank == 0 else csize,
         global_shape=GLOBAL_SHAPE,
-        chunk_shape=(csize, GLOBAL_SHAPE[1], GLOBAL_SHAPE[2])
+        chunk_shape=(csize, GLOBAL_SHAPE[1], GLOBAL_SHAPE[2]),
     )
 
     with h5py.File(tmp_path / "test_file.h5", "w", driver="mpio", comm=comm) as file:
diff --git a/tests/test_mpiutil.py b/tests/test_mpiutil.py
index 1c4b1c489..3ac84b5fa 100644
--- a/tests/test_mpiutil.py
+++ b/tests/test_mpiutil.py
@@ -8,9 +8,7 @@
 @pytest.mark.mpi
 def test_all_to_all():
     comm = MPI.COMM_WORLD
-    data = [
-        np.ones((5, 5, 5), dtype=np.uint16) * comm.rank for _ in range(comm.size)
-    ]
+    data = [np.ones((5, 5, 5), dtype=np.uint16) * comm.rank for _ in range(comm.size)]
     rec = alltoall(data, comm)
 
     expected = [np.ones((5, 5, 5), dtype=np.uint16) * r for r in range(comm.size)]
diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py
index b33b7c287..526205b76 100644
--- a/tests/test_pipeline.py
+++ b/tests/test_pipeline.py
@@ -1,5 +1,3 @@
-import glob
-import os
 import re
 import subprocess
 from typing import Callable, List, Tuple
@@ -48,7 +46,13 @@ def _check_tif(files: List, number: int, shape: Tuple):
 
 
 def test_tomo_standard_testing_pipeline_output(
-    get_files: Callable, cmd, standard_data, standard_loader, testing_pipeline, output_folder, merge_yamls
+    get_files: Callable,
+    cmd,
+    standard_data,
+    standard_loader,
+    testing_pipeline,
+    output_folder,
+    merge_yamls,
 ):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
@@ -81,7 +85,7 @@ def test_tomo_standard_testing_pipeline_output(
     #: It will be worth moving the unit tests for the logger to a separate file
     #: once we generate different log files for each MPI process and we can compare them.
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
 
@@ -95,7 +99,9 @@ def test_tomo_standard_testing_pipeline_output(
     assert "Data shape is (180, 3, 160) of type uint16" in verbose_log_contents
 
 
-def test_run_pipeline_cpu1_yaml(get_files: Callable, cmd, standard_data, yaml_cpu_pipeline1, output_folder):
+def test_run_pipeline_cpu1_yaml(
+    get_files: Callable, cmd, standard_data, yaml_cpu_pipeline1, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, yaml_cpu_pipeline1)
@@ -125,7 +131,9 @@ def test_run_pipeline_cpu1_yaml(get_files: Callable, cmd, standard_data, yaml_cp
     assert "Data shape is (180, 128, 160) of type uint16" in verbose_log_contents
 
 
-def test_run_pipeline_cpu1_py(get_files: Callable, cmd, standard_data, python_cpu_pipeline1, output_folder):
+def test_run_pipeline_cpu1_py(
+    get_files: Callable, cmd, standard_data, python_cpu_pipeline1, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, python_cpu_pipeline1)
@@ -139,7 +147,7 @@ def test_run_pipeline_cpu1_py(get_files: Callable, cmd, standard_data, python_cp
     _check_tif(files, 128, (160, 160))
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
@@ -152,7 +160,9 @@ def test_run_pipeline_cpu1_py(get_files: Callable, cmd, standard_data, python_cp
     assert "Data shape is (180, 128, 160) of type uint16" in verbose_log_contents
 
 
-def test_run_pipeline_cpu2_yaml(get_files: Callable, cmd, standard_data, yaml_cpu_pipeline2, output_folder):
+def test_run_pipeline_cpu2_yaml(
+    get_files: Callable, cmd, standard_data, yaml_cpu_pipeline2, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, yaml_cpu_pipeline2)
@@ -166,7 +176,7 @@ def test_run_pipeline_cpu2_yaml(get_files: Callable, cmd, standard_data, yaml_cp
     _check_tif(files, 30, (160, 160))
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
 
@@ -181,7 +191,6 @@ def test_run_pipeline_cpu2_yaml(get_files: Callable, cmd, standard_data, yaml_cp
                 assert f["data"].dtype == np.float32
                 assert_allclose(np.sum(f["data"]), 694.70306, atol=1e-6, rtol=1e-6)
 
-    
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
 
     assert f"{user_log_file[0]}" in verbose_log_contents
@@ -192,7 +201,9 @@ def test_run_pipeline_cpu2_yaml(get_files: Callable, cmd, standard_data, yaml_cp
     assert "Data shape is (180, 30, 160) of type uint16" in verbose_log_contents
 
 
-def test_run_pipeline_cpu2_py(get_files: Callable, cmd, standard_data, python_cpu_pipeline2, output_folder):
+def test_run_pipeline_cpu2_py(
+    get_files: Callable, cmd, standard_data, python_cpu_pipeline2, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, python_cpu_pipeline2)
@@ -217,7 +228,7 @@ def test_run_pipeline_cpu2_py(get_files: Callable, cmd, standard_data, python_cp
                 assert_allclose(np.sum(f["data"]), 694.70306, atol=1e-6, rtol=1e-6)
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
@@ -230,7 +241,9 @@ def test_run_pipeline_cpu2_py(get_files: Callable, cmd, standard_data, python_cp
     assert "Data shape is (180, 30, 160) of type uint16" in verbose_log_contents
 
 
-def test_run_pipeline_cpu3_yaml(get_files: Callable, cmd, standard_data, yaml_cpu_pipeline3, output_folder):
+def test_run_pipeline_cpu3_yaml(
+    get_files: Callable, cmd, standard_data, yaml_cpu_pipeline3, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, yaml_cpu_pipeline3)
@@ -248,7 +261,7 @@ def test_run_pipeline_cpu3_yaml(get_files: Callable, cmd, standard_data, yaml_cp
     assert len(h5_files) == 1
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
@@ -264,7 +277,9 @@ def test_run_pipeline_cpu3_yaml(get_files: Callable, cmd, standard_data, yaml_cp
     assert " Global mean 0.0016174" in verbose_log_contents
 
 
-def test_run_pipeline_cpu3_py(get_files: Callable, cmd, standard_data, python_cpu_pipeline3, output_folder):
+def test_run_pipeline_cpu3_py(
+    get_files: Callable, cmd, standard_data, python_cpu_pipeline3, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, python_cpu_pipeline3)
@@ -282,7 +297,7 @@ def test_run_pipeline_cpu3_py(get_files: Callable, cmd, standard_data, python_cp
     assert len(h5_files) == 1
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
@@ -298,7 +313,9 @@ def test_run_pipeline_cpu3_py(get_files: Callable, cmd, standard_data, python_cp
     assert " Global mean 0.0016174" in verbose_log_contents
 
 
-def test_run_pipeline_cpu4_yaml(get_files: Callable, cmd, standard_data, yaml_cpu_pipeline4, output_folder):
+def test_run_pipeline_cpu4_yaml(
+    get_files: Callable, cmd, standard_data, yaml_cpu_pipeline4, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, yaml_cpu_pipeline4)
@@ -332,7 +349,9 @@ def test_run_pipeline_cpu4_yaml(get_files: Callable, cmd, standard_data, yaml_cp
 
 
 @pytest.mark.cupy
-def test_run_pipeline_gpu1_yaml(get_files: Callable, cmd, standard_data, yaml_gpu_pipeline1, output_folder):
+def test_run_pipeline_gpu1_yaml(
+    get_files: Callable, cmd, standard_data, yaml_gpu_pipeline1, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, yaml_gpu_pipeline1)
@@ -357,7 +376,7 @@ def test_run_pipeline_gpu1_yaml(get_files: Callable, cmd, standard_data, yaml_gp
                 assert_allclose(np.sum(f["data"]), 2615.7332, atol=1e-6, rtol=1e-6)
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
@@ -369,11 +388,15 @@ def test_run_pipeline_gpu1_yaml(get_files: Callable, cmd, standard_data, yaml_gp
     assert "Preview: (0:180, 0:128, 0:160)" in verbose_log_contents
     assert "Data shape is (180, 128, 160) of type uint16" in verbose_log_contents
     assert "The amount of the available GPU memory is" in verbose_log_contents
-    assert "Using GPU 0 to transfer data of shape (180, 128, 160)" in verbose_log_contents
+    assert (
+        "Using GPU 0 to transfer data of shape (180, 128, 160)" in verbose_log_contents
+    )
 
 
 @pytest.mark.cupy
-def test_run_pipeline_gpu1_py(get_files: Callable, cmd, standard_data, python_gpu_pipeline1, output_folder):
+def test_run_pipeline_gpu1_py(
+    get_files: Callable, cmd, standard_data, python_gpu_pipeline1, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, standard_data)
     cmd.insert(7, python_gpu_pipeline1)
@@ -398,7 +421,7 @@ def test_run_pipeline_gpu1_py(get_files: Callable, cmd, standard_data, python_gp
                 assert_allclose(np.sum(f["data"]), 2615.7332, atol=1e-6, rtol=1e-6)
 
     verbose_log_file = list(filter(lambda x: "debug.log" in x, files))
-    user_log_file  = list(filter(lambda x: "user.log" in x, files))
+    user_log_file = list(filter(lambda x: "user.log" in x, files))
     assert len(verbose_log_file) == 1
     assert len(user_log_file) == 1
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
@@ -410,11 +433,19 @@ def test_run_pipeline_gpu1_py(get_files: Callable, cmd, standard_data, python_gp
     assert "Preview: (0:180, 0:128, 0:160)" in verbose_log_contents
     assert "Data shape is (180, 128, 160) of type uint16" in verbose_log_contents
     assert "The amount of the available GPU memory is" in verbose_log_contents
-    assert "Using GPU 0 to transfer data of shape (180, 128, 160)" in verbose_log_contents
+    assert (
+        "Using GPU 0 to transfer data of shape (180, 128, 160)" in verbose_log_contents
+    )
 
 
 def test_tomo_standard_testing_pipeline_output_with_save_all(
-    get_files: Callable, cmd, standard_data, standard_loader, testing_pipeline, output_folder, merge_yamls
+    get_files: Callable,
+    cmd,
+    standard_data,
+    standard_loader,
+    testing_pipeline,
+    output_folder,
+    merge_yamls,
 ):
     cmd.insert(7, standard_data)
     merge_yamls(standard_loader, testing_pipeline)
@@ -426,7 +457,7 @@ def test_tomo_standard_testing_pipeline_output_with_save_all(
     assert len(files) == 10
 
     _check_yaml(files, "temp.yaml")
-    _check_tif(files, 3, (160,160))
+    _check_tif(files, 3, (160, 160))
 
     #: check the generated h5 files
     h5_files = list(filter(lambda x: ".h5" in x, files))
@@ -442,7 +473,13 @@ def test_tomo_standard_testing_pipeline_output_with_save_all(
 
 
 def test_i12_testing_pipeline_output(
-    get_files: Callable, cmd, i12_data, i12_loader, testing_pipeline, output_folder, merge_yamls
+    get_files: Callable,
+    cmd,
+    i12_data,
+    i12_loader,
+    testing_pipeline,
+    output_folder,
+    merge_yamls,
 ):
     cmd.insert(7, i12_data)
     merge_yamls(i12_loader, testing_pipeline)
@@ -548,7 +585,13 @@ def test_i12_testing_pipeline_output(
 
 
 def test_diad_testing_pipeline_output(
-    get_files: Callable, cmd, diad_data, diad_loader, testing_pipeline, output_folder, merge_yamls
+    get_files: Callable,
+    cmd,
+    diad_data,
+    diad_loader,
+    testing_pipeline,
+    output_folder,
+    merge_yamls,
 ):
     cmd.insert(7, diad_data)
     merge_yamls(diad_loader, testing_pipeline)
@@ -585,14 +628,18 @@ def test_diad_testing_pipeline_output(
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
 
     assert "The full dataset shape is (3201, 22, 26)" in verbose_log_contents
-    assert "Loading data: tests/test_data/k11_diad/k11-18014.nxs" in verbose_log_contents
+    assert (
+        "Loading data: tests/test_data/k11_diad/k11-18014.nxs" in verbose_log_contents
+    )
     assert "Path to data: /entry/imaging/data" in verbose_log_contents
     assert "Preview: (100:3101, 5:7, 0:26)" in verbose_log_contents
     assert "Data shape is (3001, 2, 26) of type uint16" in verbose_log_contents
 
 
 @pytest.mark.cupy
-def test_run_diad_pipeline_gpu(get_files: Callable, cmd, diad_data, diad_pipeline_gpu, output_folder):
+def test_run_diad_pipeline_gpu(
+    get_files: Callable, cmd, diad_data, diad_pipeline_gpu, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, diad_data)
     cmd.insert(7, diad_pipeline_gpu)
@@ -613,17 +660,21 @@ def test_run_diad_pipeline_gpu(get_files: Callable, cmd, diad_data, diad_pipelin
     verbose_log_contents = _get_log_contents(verbose_log_file[0])
 
     assert "The full dataset shape is (3201, 22, 26)" in verbose_log_contents
-    assert "Loading data: tests/test_data/k11_diad/k11-18014.nxs" in verbose_log_contents
+    assert (
+        "Loading data: tests/test_data/k11_diad/k11-18014.nxs" in verbose_log_contents
+    )
     assert "Path to data: /entry/imaging/data" in verbose_log_contents
     assert "Preview: (100:3101, 8:15, 0:26)" in verbose_log_contents
     assert "Data shape is (3001, 7, 26) of type uint16" in verbose_log_contents
-    assert "Global min -0.011995" in verbose_log_contents
-    assert "Global max 0.019879" in verbose_log_contents
-    assert "Global mean 0.000291" in verbose_log_contents
+    assert "Global min -0.0326580" in verbose_log_contents
+    assert "Global max 0.037757" in verbose_log_contents
+    assert "Global mean 0.000327" in verbose_log_contents
 
 
 @pytest.mark.cupy
-def test_run_pipeline_360deg_gpu2(get_files: Callable, cmd, data360, yaml_gpu_pipeline360_2, output_folder):
+def test_run_pipeline_360deg_gpu2(
+    get_files: Callable, cmd, data360, yaml_gpu_pipeline360_2, output_folder
+):
     cmd.pop(4)  #: don't save all
     cmd.insert(6, data360)
     cmd.insert(7, yaml_gpu_pipeline360_2)
@@ -647,6 +698,6 @@ def test_run_pipeline_360deg_gpu2(get_files: Callable, cmd, data360, yaml_gpu_pi
     assert "Loading data: tests/test_data/360scan/360scan.hdf" in verbose_log_contents
     assert "Path to data: entry1/tomo_entry/data/data" in verbose_log_contents
     assert "Data shape is (3601, 3, 2560) of type uint16" in verbose_log_contents
-    assert "Global min -0.00315" in verbose_log_contents
-    assert "Global max 0.00575" in verbose_log_contents
-    assert "Global mean 0.00088" in verbose_log_contents
+    assert "Global min -0.003281" in verbose_log_contents
+    assert "Global max 0.006374" in verbose_log_contents
+    assert "Global mean 0.000887" in verbose_log_contents
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 6769b5be1..6527f2ebb 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -5,7 +5,7 @@
 def test_cachetime():
     with catchtime() as t:
         sleep(0.1)
-        
+
     assert t.elapsed >= 0.1
     assert t.elapsed < 0.2
 
@@ -14,8 +14,8 @@ def test_catch_gputime():
     input = xp.ones((500, 200, 100), dtype=xp.float32)
     with catch_gputime() as t:
         xp.sum(input)
-        
+
     if gpu_enabled:
         assert t.elapsed > 0.0
     else:
-        assert t.elapsed == 0.0
\ No newline at end of file
+        assert t.elapsed == 0.0
diff --git a/tests/test_yaml_checker.py b/tests/test_yaml_checker.py
index b8af9f5b3..c2df2f0ab 100644
--- a/tests/test_yaml_checker.py
+++ b/tests/test_yaml_checker.py
@@ -1,6 +1,7 @@
 """
 Some unit tests for the yaml checker
 """
+
 from typing import Callable
 
 import pytest
@@ -35,91 +36,52 @@ def test_sanity_check(sample_pipelines):
         assert not sanity_check(conf_generator)
 
 
-def test_check_first_method_is_loader(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    no_loader_method_pipeline = (
-        sample_pipelines + "testing/no_loader_method.yaml"
-    )
+def test_check_first_method_is_loader(sample_pipelines: str, load_yaml: Callable):
+    no_loader_method_pipeline = sample_pipelines + "testing/no_loader_method.yaml"
     conf = load_yaml(no_loader_method_pipeline)
     assert not check_first_method_is_loader(conf)
 
 
 def test_hdf5_paths_against_loader(
-        standard_data,
-        sample_pipelines,
-        load_yaml: Callable
+    standard_data, sample_pipelines, load_yaml: Callable
 ):
-    incorrect_path_pipeline = (
-        sample_pipelines + "testing/incorrect_path.yaml"
-    )
+    incorrect_path_pipeline = sample_pipelines + "testing/incorrect_path.yaml"
     conf = load_yaml(incorrect_path_pipeline)
     assert not check_hdf5_paths_against_loader(conf, standard_data)
 
 
-def test_check_methods_exist_in_templates(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    incorrect_method_pipeline = (
-        sample_pipelines + "testing/incorrect_method.yaml"
-    )
+def test_check_methods_exist_in_templates(sample_pipelines: str, load_yaml: Callable):
+    incorrect_method_pipeline = sample_pipelines + "testing/incorrect_method.yaml"
     conf = load_yaml(incorrect_method_pipeline)
     assert not check_methods_exist_in_templates(conf)
 
 
 @pytest.mark.skip(reason="Some parameters are additional and not listed in templates")
-def test_check_parameter_names_are_known(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    required_param_pipeline = (
-        sample_pipelines + "testing/unknown_param.yaml"
-    )
+def test_check_parameter_names_are_known(sample_pipelines: str, load_yaml: Callable):
+    required_param_pipeline = sample_pipelines + "testing/unknown_param.yaml"
     conf = load_yaml(required_param_pipeline)
     assert not check_parameter_names_are_known(conf)
 
 
-def test_check_parameter_names_are_str(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    required_param_pipeline = (
-        sample_pipelines + "testing/non_str_param_name.yaml"
-    )
+def test_check_parameter_names_are_str(sample_pipelines: str, load_yaml: Callable):
+    required_param_pipeline = sample_pipelines + "testing/non_str_param_name.yaml"
     conf = load_yaml(required_param_pipeline)
     assert not check_parameter_names_are_str(conf)
 
 
-def test_check_no_required_parameter_values(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    required_param_pipeline = (
-        sample_pipelines + "testing/required_param.yaml"
-    )
+def test_check_no_required_parameter_values(sample_pipelines: str, load_yaml: Callable):
+    required_param_pipeline = sample_pipelines + "testing/required_param.yaml"
     conf = load_yaml(required_param_pipeline)
     assert not check_no_required_parameter_values(conf)
 
 
-def test_check_no_duplicated_keys(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    required_param_pipeline = (
-        sample_pipelines + "testing/duplicated_key.yaml"
-    )
+def test_check_no_duplicated_keys(sample_pipelines: str, load_yaml: Callable):
+    required_param_pipeline = sample_pipelines + "testing/duplicated_key.yaml"
     assert not check_no_duplicated_keys(required_param_pipeline)
 
 
-def test_check_keys(
-        sample_pipelines: str,
-        load_yaml: Callable
-):
-    required_param_pipeline = (
-        sample_pipelines + "testing/required_keys.yaml"
-    )
+def test_check_keys(sample_pipelines: str, load_yaml: Callable):
+    required_param_pipeline = sample_pipelines + "testing/required_keys.yaml"
     conf = load_yaml(required_param_pipeline)
     assert not check_keys(conf)
 
@@ -194,12 +156,7 @@ def test_check_side_out_matches_ref_arg(
     ],
 )
 def test_validate_yaml_config(
-    sample_pipelines: str,
-    yaml_file: str,
-    standard_data: str,
-    expected: bool
+    sample_pipelines: str, yaml_file: str, standard_data: str, expected: bool
 ):
     yaml_file = sample_pipelines + yaml_file
     assert validate_yaml_config(yaml_file, standard_data) == expected
-
-