diff --git a/.github/workflows/sanitize.yml b/.github/workflows/sanitize.yml
index 3d6ee63de21e6cb67c2109789be9fe413b3d5d1c..1b40b7f0c20d9dabe20772c6623edeed3fc7410f 100644
--- a/.github/workflows/sanitize.yml
+++ b/.github/workflows/sanitize.yml
@@ -7,124 +7,46 @@ on:
 jobs:
   build:
     name: "Sanitize"
-    runs-on: ${{ matrix.os }}
+    runs-on: ubuntu-20.04
     strategy:
       fail-fast: false
       matrix:
-        name:      ["Linux Clang -fsanitize ${{ matrix.sanitizer }}"]
-        os:        ["ubuntu-20.04"]
-        cc:        ["clang-10"]
-        cxx:       ["clang++-10"]
-        py:        ["3.9"]
-        cmake:     ["3.19.x"]
-        mpi:       ["ON"]
+        name:      ["Sanitize"]
+        sanitizer: ["address", "undefined", "thread"]
         simd:      ["ON", "OFF"]
-        sanitizer: ["address", "leak", "undefined", "memory", "thread"]
     env:
-        CC:         ${{ matrix.cc }}
-        CXX:        ${{ matrix.cxx }}
-        # We set PYTHONPATH instead of installing arbor to avoid distribution/OS specific behaviour.
-        PYTHONPATH: ${{ github.workspace }}/build/python
+        CC:           clang-10
+        CXX:          clang++-10
+        ASAN_OPTIONS: detect_leaks=1
     steps:
       - name: Set up cmake
         uses: jwlawson/actions-setup-cmake@v1.7
         with:
-          cmake-version: ${{ matrix.cmake }}
-      - name: Set up Python
-        uses: actions/setup-python@v2
-        with:
-          python-version: ${{ matrix.py }}
-      - name: OpenMPI cache
-        uses: actions/cache@v2
-        id:   cache-ompi
-        with:
-          path: ~/openmpi-4.0.2
-          key:  ${{ matrix.os }}-openmpi-4.0.2-${{ matrix.cxx }}
-      - name: Build OpenMPI
-        if: ${{ steps.cache-ompi.outputs.cache-hit != 'true' }}
-        run: |
-           echo cache-hit='${{ steps.cache-ompi.outputs.cache-hit }}'
-           cd ~
-           wget https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.2.tar.gz
-           tar -xvf ./openmpi-4.0.2.tar.gz
-           cd openmpi-4.0.2
-           ./configure --disable-mpi-fortran
-           make -j4
-      - name: Install OpenMPI
-        run: |
-           echo "Going to install ompi"
-           cd ~
-           cd openmpi-4.0.2
-           sudo make install
-           cd -
-      - name: Update shared library cache
-        if: ${{ startsWith(matrix.os, 'ubuntu') }}
-        run: sudo ldconfig
-      - name: Install Python packages
-        run:  pip install numpy sphinx svgwrite sphinx-rtd-theme mpi4py pandas seaborn
+          cmake-version: 3.19.x
       - name: Clone w/ submodules
         uses: actions/checkout@v2
         with:
           submodules: recursive
-      - name: Check config
-        run: |
-          $CC --version
-          $CXX --version
-          python --version
-          mpic++ --show
-          mpicc --show
-          echo $PYTHONPATH
       - name: Build arbor
         run: |
           mkdir build
           cd build
-          cmake .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_CXX_FLAGS="-fsanitize=${{ matrix.sanitizer }} -fno-omit-frame-pointer" -DCMAKE_C_FLAGS="-fsanitize=${{ matrix.sanitizer }} -fno-omit-frame-pointer" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=${{ matrix.sanitizer }}" -DCMAKE_MODULE/_LINKER_FLAGS="-fsanitize=${{ matrix.sanitizer }}" -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_WITH_PYTHON=ON -DARB_VECTORIZE=${{ matrix.simd }} -DPython3_EXECUTABLE=`which python` -DARB_WITH_MPI=${{ matrix.mpi }} -DARB_USE_BUNDLED_LIBS=ON
-          make -j4 tests examples pyarb html
+          export SAN="-fsanitize=${{ matrix.sanitizer }} -fno-omit-frame-pointer"
+          cmake .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_CXX_FLAGS="$SAN" -DCMAKE_C_FLAGS="$SAN" -DCMAKE_EXE_LINKER_FLAGS="$SAN" -DCMAKE_MODULE_LINKER_FLAGS="$SAN" -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_VECTORIZE=${{ matrix.simd }} -DARB_WITH_MPI=OFF -DARB_USE_BUNDLED_LIBS=ON -DARB_WITH_PYTHON=ON -DPython3_EXECUTABLE=`which python`
+          make -j4 VERBOSE=1 tests examples pyarb
           cd -
       - name: Run unit tests
         run: |
-          build/bin/unit
+          build/bin/unit --gtest_filter=-*DeathTest
           build/bin/unit-modcc
-      - if:   ${{ matrix.mpi == 'ON' }}
-        name: Run MPI tests
-        run:  mpirun -n 4 -oversubscribe build/bin/unit-mpi
-      - if:   ${{ matrix.mpi == 'OFF' }}
-        name: Run examples
-        run: |
-            build/bin/bench
-            build/bin/brunel
-            build/bin/dryrun
-            build/bin/gap_junctions
-            build/bin/generators
-            build/bin/lfp
-            build/bin/probe-demo v
-            build/bin/ring
-            build/bin/single-cell
-      - if:   ${{ matrix.mpi == 'ON' }}
-        name: Run examples with MPI
-        run: |
-            mpirun -n 4 -oversubscribe build/bin/bench
-            mpirun -n 4 -oversubscribe build/bin/brunel
-            mpirun -n 4 -oversubscribe build/bin/dryrun
-            mpirun -n 4 -oversubscribe build/bin/gap_junctions
-            mpirun -n 4 -oversubscribe build/bin/generators
-            mpirun -n 4 -oversubscribe build/bin/lfp
-            mpirun -n 4 -oversubscribe build/bin/probe-demo v
-            mpirun -n 4 -oversubscribe build/bin/ring
-            mpirun -n 4 -oversubscribe build/bin/single-cell
-      - name: Run python tests
-        run: python python/test/unit/runner.py -v2
-      - if:   ${{ matrix.mpi == 'ON' }}
-        name: Run python+MPI tests
-        run:  mpirun -n 4 -oversubscribe python python/test/unit_distributed/runner.py -v2
-      - name: Run Python examples
-        run: |
-            python python/example/network_ring.py
-            python python/example/single_cell_model.py
-            python python/example/single_cell_recipe.py
-            python python/example/single_cell_stdp.py
-            python python/example/brunel.py -n 400 -m 100 -e 20 -p 0.1 -w 1.2 -d 1 -g 0.5 -l 5 -t 100 -s 1 -G 50 -S 123
-            python python/example/single_cell_swc.py python/example/single_cell_detailed.swc
-            python python/example/single_cell_detailed.py python/example/single_cell_detailed.swc
-            python python/example/single_cell_detailed_recipe.py python/example/single_cell_detailed.swc
-            python python/example/single_cell_cable.py
+      - name: Run examples
+        run: |
+          build/bin/bench
+          build/bin/brunel
+          build/bin/dryrun
+          build/bin/gap_junctions
+          build/bin/generators
+          build/bin/lfp
+          build/bin/probe-demo v
+          build/bin/ring
+          build/bin/single-cell
diff --git a/example/dryrun/dryrun.cpp b/example/dryrun/dryrun.cpp
index 81773a97c143f771cb3ee30a428530cbceca35da..8a057d0972f9a00644212593f9d3fca69c85c74d 100644
--- a/example/dryrun/dryrun.cpp
+++ b/example/dryrun/dryrun.cpp
@@ -47,6 +47,7 @@ struct run_params {
     double min_delay = 10;
     double duration = 100;
     cell_parameters cell;
+    bool defaulted = true;
 };
 
 void write_trace_json(const arb::trace_data<double>& trace);
@@ -146,15 +147,10 @@ int main(int argc, char** argv) {
 #ifdef ARB_MPI_ENABLED
         else {
             ctx = arb::make_context(resources, MPI_COMM_WORLD);
-            {
-                int rank;
-                MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-                root = rank==0;
-            }
+            if (params.defaulted) params.num_ranks = arb::num_ranks(ctx);
+            root = arb::rank(ctx)==0;
         }
 #endif
-        assert(arb::num_ranks(ctx)==params.num_ranks);
-
 
 #ifdef ARB_PROFILE_ENABLED
         arb::profile::profiler_initialize(ctx);
@@ -165,10 +161,11 @@ int main(int argc, char** argv) {
         std::cout << "gpu:      " << (has_gpu(ctx)? "yes": "no") << "\n";
         std::cout << "threads:  " << num_threads(ctx) << "\n";
         std::cout << "mpi:      " << (has_mpi(ctx)? "yes": "no") << "\n";
-        std::cout << "ranks:    " << num_ranks(ctx) << "\n" << std::endl;
-
+        std::cout << "ranks:    " << num_ranks(ctx) << "(" << params.num_ranks << ")\n" << std::endl;
         std::cout << "run mode: " << distribution_type(ctx) << "\n";
 
+        assert(arb::num_ranks(ctx)==params.num_ranks);
+
         arb::profile::meter_manager meters;
         meters.start(ctx);
 
@@ -273,6 +270,9 @@ run_params read_options(int argc, char** argv) {
         std::cout << "Using default parameters.\n";
         return params;
     }
+    else {
+        params.defaulted = false;
+    }
     if (argc>2) {
         throw std::runtime_error("More than one command line option is not permitted.");
     }
diff --git a/test/unit/instrument_malloc.hpp b/test/unit/instrument_malloc.hpp
index 825d4df92bdb591c802e51cf45e9438cee744352..62efc835a4b18d15164ecb72fea134d3ca89a658 100644
--- a/test/unit/instrument_malloc.hpp
+++ b/test/unit/instrument_malloc.hpp
@@ -32,6 +32,9 @@
 #if __has_feature(address_sanitizer)
 #undef CAN_INSTRUMENT_MALLOC
 #endif
+#  if __has_feature(thread_sanitizer)
+#undef CAN_INSTRUMENT_MALLOC
+#  endif
 #endif
 // This is how gcc tells us.
 #if defined(__SANITIZE_ADDRESS__)