From 7b0b69f3eae977a7ac0e4d2ae32b825456887255 Mon Sep 17 00:00:00 2001
From: noraabiakar <nora.abiakar@gmail.com>
Date: Mon, 18 Feb 2019 16:44:11 +0100
Subject: [PATCH] Integrate over integration-domains instead of cells in the
 lowered/back-end implementation (#688)

Fixes #683.

* Distinguish between per-cell matrix solver and per-'integration domain' state in the FVM back-end, adding new index for FVM back-ends mapping CVs to integration domain index.
* Move to one event queue per integration domain, avoiding extra logic for synchronisation of times across gap-junction connected cells. This requires a merging of event lanes in `mc_cell_group` when constructing the staged events in the multi-event queue.
* Add synapses to gap junction example.
---
 arbor/backends/event.hpp                      |   8 +-
 arbor/backends/gpu/fvm.hpp                    |   2 +-
 arbor/backends/gpu/matrix_assemble.cu         |  20 +-
 arbor/backends/gpu/matrix_fine.cu             |  10 +-
 arbor/backends/gpu/matrix_fine.hpp            |   3 +-
 arbor/backends/gpu/matrix_state_fine.hpp      |  13 +-
 arbor/backends/gpu/matrix_state_flat.hpp      |  21 +-
 .../backends/gpu/matrix_state_interleaved.hpp |  19 +-
 arbor/backends/gpu/mechanism.cpp              |   2 +-
 arbor/backends/gpu/shared_state.cpp           |  40 ++--
 arbor/backends/gpu/shared_state.cu            |  39 +--
 arbor/backends/gpu/shared_state.hpp           |  21 +-
 arbor/backends/gpu/threshold_watcher.cu       |   8 +-
 arbor/backends/gpu/threshold_watcher.hpp      |  10 +-
 arbor/backends/multicore/fvm.hpp              |   2 +-
 arbor/backends/multicore/matrix_state.hpp     |  14 +-
 arbor/backends/multicore/mechanism.cpp        |   2 +-
 arbor/backends/multicore/shared_state.cpp     |  59 ++---
 arbor/backends/multicore/shared_state.hpp     |  21 +-
 .../backends/multicore/threshold_watcher.hpp  |   8 +-
 arbor/fvm_layout.cpp                          |   1 +
 arbor/fvm_lowered_cell.hpp                    |   2 +-
 arbor/fvm_lowered_cell_impl.hpp               |  81 ++++++-
 arbor/matrix.hpp                              |   9 +-
 arbor/mc_cell_group.cpp                       |  96 +++-----
 arbor/mc_cell_group.hpp                       |  18 +-
 example/gap_junctions/gap_junctions.cpp       |  76 ++++--
 example/gap_junctions/parameters.hpp          |  22 +-
 example/gap_junctions/readme.md               |  42 ++++
 test/unit/CMakeLists.txt                      |   1 -
 test/unit/test_fvm_lowered.cpp                | 226 ++++++++++++++++--
 test/unit/test_matrix.cpp                     |  12 +-
 test/unit/test_matrix.cu                      |   8 +-
 test/unit/test_matrix_cpuvsgpu.cpp            |   6 +-
 test/unit/test_mc_cell_group.cpp              | 149 ------------
 test/unit/test_mech_temperature.cpp           |   5 +-
 test/unit/test_probe.cpp                      |   3 +-
 test/unit/test_supercell.cpp                  |  71 ------
 test/unit/test_synapses.cpp                   |   4 +-
 39 files changed, 602 insertions(+), 552 deletions(-)
 delete mode 100644 test/unit/test_supercell.cpp

diff --git a/arbor/backends/event.hpp b/arbor/backends/event.hpp
index deba95ee..cdf4b5cc 100644
--- a/arbor/backends/event.hpp
+++ b/arbor/backends/event.hpp
@@ -13,11 +13,11 @@ namespace arb {
 struct target_handle {
     cell_local_size_type mech_id;    // mechanism type identifier (per cell group).
     cell_local_size_type mech_index; // instance of the mechanism
-    cell_size_type cell_index;       // which cell (acts as index into e.g. vec_t)
+    cell_size_type intdom_index;       // which integration domain (acts as index into e.g. vec_t)
 
     target_handle() {}
-    target_handle(cell_local_size_type mech_id, cell_local_size_type mech_index, cell_size_type cell_index):
-        mech_id(mech_id), mech_index(mech_index), cell_index(cell_index) {}
+    target_handle(cell_local_size_type mech_id, cell_local_size_type mech_index, cell_size_type intdom_index):
+        mech_id(mech_id), mech_index(mech_index), intdom_index(intdom_index) {}
 };
 
 struct deliverable_event {
@@ -33,7 +33,7 @@ struct deliverable_event {
 
 // Stream index accessor function for multi_event_stream:
 inline cell_size_type event_index(const deliverable_event& ev) {
-    return ev.handle.cell_index;
+    return ev.handle.intdom_index;
 }
 
 // Subset of event information required for mechanism delivery.
diff --git a/arbor/backends/gpu/fvm.hpp b/arbor/backends/gpu/fvm.hpp
index cc82bed0..f19a71bd 100644
--- a/arbor/backends/gpu/fvm.hpp
+++ b/arbor/backends/gpu/fvm.hpp
@@ -64,7 +64,7 @@ struct backend {
         const execution_context& context)
     {
         return threshold_watcher(
-            state.cv_to_cell.data(),
+            state.cv_to_intdom.data(),
             state.time.data(),
             state.time_to.data(),
             state.voltage.data(),
diff --git a/arbor/backends/gpu/matrix_assemble.cu b/arbor/backends/gpu/matrix_assemble.cu
index fde63e1a..bcc589d2 100644
--- a/arbor/backends/gpu/matrix_assemble.cu
+++ b/arbor/backends/gpu/matrix_assemble.cu
@@ -24,14 +24,15 @@ void assemble_matrix_flat(
         const T* cv_capacitance,
         const T* area,
         const I* cv_to_cell,
-        const T* dt_cell,
+        const T* dt_intdom,
+        const I* cell_to_intdom,
         unsigned n)
 {
     const unsigned tid = threadIdx.x + blockDim.x*blockIdx.x;
 
     if (tid<n) {
         auto cid = cv_to_cell[tid];
-        auto dt = dt_cell[cid];
+        auto dt = dt_intdom[cell_to_intdom[cid]];
 
         // Note: dt==0 case is expected only at the end of a mindelay/2
         // integration period, and consequently divergence is unlikely
@@ -73,7 +74,8 @@ void assemble_matrix_interleaved(
         const I* sizes,
         const I* starts,
         const I* matrix_to_cell,
-        const T* dt_cell,
+        const T* dt_intdom,
+        const I* cell_to_intdom,
         unsigned padded_size, unsigned num_mtx)
 {
     static_assert(BlockWidth*LoadWidth==Threads,
@@ -107,7 +109,7 @@ void assemble_matrix_interleaved(
 
     if (permuted_cid<num_mtx) {
         auto cid = matrix_to_cell[permuted_cid];
-        dt = dt_cell[cid];
+        dt = dt_intdom[cell_to_intdom[cid]];
 
         // The 1e-3 is a constant of proportionality required to ensure that the
         // conductance (gi) values have units μS (micro-Siemens).
@@ -155,7 +157,8 @@ void assemble_matrix_flat(
         const fvm_value_type* cv_capacitance,
         const fvm_value_type* area,
         const fvm_index_type* cv_to_cell,
-        const fvm_value_type* dt_cell,
+        const fvm_value_type* dt_intdom,
+        const fvm_index_type* cell_to_intdom,
         unsigned n)
 {
     constexpr unsigned block_dim = 128;
@@ -165,7 +168,7 @@ void assemble_matrix_flat(
         <fvm_value_type, fvm_index_type>
         <<<grid_dim, block_dim>>>
         (d, rhs, invariant_d, voltage, current, cv_capacitance,
-         area, cv_to_cell, dt_cell, n);
+         area, cv_to_cell, dt_intdom, cell_to_intdom, n);
 }
 
 //template <typename T, typename I, unsigned BlockWidth, unsigned LoadWidth, unsigned Threads>
@@ -180,7 +183,8 @@ void assemble_matrix_interleaved(
     const fvm_index_type* sizes,
     const fvm_index_type* starts,
     const fvm_index_type* matrix_to_cell,
-    const fvm_value_type* dt_cell,
+    const fvm_value_type* dt_intdom,
+    const fvm_index_type* cell_to_intdom,
     unsigned padded_size, unsigned num_mtx)
 {
     constexpr unsigned bd = impl::matrices_per_block();
@@ -195,7 +199,7 @@ void assemble_matrix_interleaved(
         <<<grid_dim, block_dim>>>
         (d, rhs, invariant_d, voltage, current, cv_capacitance, area,
          sizes, starts, matrix_to_cell,
-         dt_cell, padded_size, num_mtx);
+         dt_intdom, cell_to_intdom, padded_size, num_mtx);
 }
 
 } // namespace gpu
diff --git a/arbor/backends/gpu/matrix_fine.cu b/arbor/backends/gpu/matrix_fine.cu
index 764b39d5..f3ad5372 100644
--- a/arbor/backends/gpu/matrix_fine.cu
+++ b/arbor/backends/gpu/matrix_fine.cu
@@ -53,7 +53,8 @@ void assemble_matrix_fine(
         const T* cv_capacitance,
         const T* area,
         const I* cv_to_cell,
-        const T* dt_cell,
+        const T* dt_intdom,
+        const I* cell_to_intdom,
         const I* perm,
         unsigned n)
 {
@@ -61,7 +62,7 @@ void assemble_matrix_fine(
 
     if (tid<n) {
         auto cid = cv_to_cell[tid];
-        auto dt = dt_cell[cid];
+        auto dt = dt_intdom[cell_to_intdom[cid]];
 
         if (dt>0) {
             // The 1e-3 is a constant of proportionality required to ensure that the
@@ -264,7 +265,8 @@ void assemble_matrix_fine(
     const fvm_value_type* cv_capacitance,
     const fvm_value_type* area,
     const fvm_index_type* cv_to_cell,
-    const fvm_value_type* dt_cell,
+    const fvm_value_type* dt_intdom,
+    const fvm_index_type* cell_to_intdom,
     const fvm_index_type* perm,
     unsigned n)
 {
@@ -273,7 +275,7 @@ void assemble_matrix_fine(
 
     kernels::assemble_matrix_fine<<<num_blocks, block_dim>>>(
         d, rhs, invariant_d, voltage, current, cv_capacitance, area,
-        cv_to_cell, dt_cell,
+        cv_to_cell, dt_intdom, cell_to_intdom,
         perm, n);
 }
 
diff --git a/arbor/backends/gpu/matrix_fine.hpp b/arbor/backends/gpu/matrix_fine.hpp
index 547b80e2..896bb28b 100644
--- a/arbor/backends/gpu/matrix_fine.hpp
+++ b/arbor/backends/gpu/matrix_fine.hpp
@@ -58,7 +58,8 @@ void assemble_matrix_fine(
     const fvm_value_type* cv_capacitance,
     const fvm_value_type* area,
     const fvm_index_type* cv_to_cell,
-    const fvm_value_type* dt_cell,
+    const fvm_value_type* dt_intdom,
+    const fvm_index_type* cell_to_intdom,
     const fvm_index_type* perm,
     unsigned n);
 
diff --git a/arbor/backends/gpu/matrix_state_fine.hpp b/arbor/backends/gpu/matrix_state_fine.hpp
index c457fe24..f5c44cbe 100644
--- a/arbor/backends/gpu/matrix_state_fine.hpp
+++ b/arbor/backends/gpu/matrix_state_fine.hpp
@@ -104,6 +104,8 @@ public:
     // for storing the solution in unpacked format
     array solution_;
 
+    iarray cell_to_intdom;
+
     // the maximum nuber of branches in each level per block
     unsigned max_branches_per_level;
 
@@ -139,7 +141,8 @@ public:
                  const std::vector<size_type>& cell_cv_divs,
                  const std::vector<value_type>& cap,
                  const std::vector<value_type>& face_conductance,
-                 const std::vector<value_type>& area)
+                 const std::vector<value_type>& area,
+                 const std::vector<size_type>& cell_intdom)
     {
         using util::make_span;
         constexpr unsigned npos = unsigned(-1);
@@ -421,6 +424,7 @@ public:
         // to be stored in flat format
         cv_capacitance = memory::make_const_view(cap);
         invariant_d = memory::make_const_view(invariant_d_tmp);
+        cell_to_intdom = memory::make_const_view(cell_intdom);
 
         // calculte the cv -> cell mappings
         std::vector<size_type> cv_to_cell_tmp(matrix_size);
@@ -434,10 +438,10 @@ public:
 
     // Assemble the matrix
     // Afterwards the diagonal and RHS will have been set given dt, voltage and current
-    //   dt_cell [ms] (per cell)
+    //   dt_intdom [ms] (per cell)
     //   voltage [mV]
     //   current [nA]
-    void assemble(const_view dt_cell, const_view voltage, const_view current) {
+    void assemble(const_view dt_intdom, const_view voltage, const_view current) {
         assemble_matrix_fine(
             d.data(),
             rhs.data(),
@@ -447,7 +451,8 @@ public:
             cv_capacitance.data(),
             cv_area.data(),
             cv_to_cell.data(),
-            dt_cell.data(),
+            dt_intdom.data(),
+            cell_to_intdom.data(),
             perm.data(),
             size());
     }
diff --git a/arbor/backends/gpu/matrix_state_flat.hpp b/arbor/backends/gpu/matrix_state_flat.hpp
index e4ddc666..6f4cbd01 100644
--- a/arbor/backends/gpu/matrix_state_flat.hpp
+++ b/arbor/backends/gpu/matrix_state_flat.hpp
@@ -30,7 +30,8 @@ void assemble_matrix_flat(
     const fvm_value_type* cv_capacitance,
     const fvm_value_type* cv_area,
     const fvm_index_type* cv_to_cell,
-    const fvm_value_type* dt_cell,
+    const fvm_value_type* dt_intdom,
+    const fvm_index_type* cell_to_intdom,
     unsigned n);
 
 /// matrix state
@@ -57,6 +58,8 @@ struct matrix_state_flat {
     array face_conductance;  // [μS]
     array cv_area;           // [μm^2]
 
+    iarray cell_to_intdom;
+
     // the invariant part of the matrix diagonal
     array invariant_d;         // [μS]
 
@@ -66,7 +69,8 @@ struct matrix_state_flat {
                  const std::vector<index_type>& cell_cv_divs,
                  const std::vector<value_type>& cv_cap,
                  const std::vector<value_type>& face_cond,
-                 const std::vector<value_type>& area):
+                 const std::vector<value_type>& area,
+                 const std::vector<index_type>& cell_to_intdom):
         parent_index(memory::make_const_view(p)),
         cell_cv_divs(memory::make_const_view(cell_cv_divs)),
         cv_to_cell(p.size()),
@@ -74,7 +78,8 @@ struct matrix_state_flat {
         u(p.size()),
         rhs(p.size()),
         cv_capacitance(memory::make_const_view(cv_cap)),
-        cv_area(memory::make_const_view(area))
+        cv_area(memory::make_const_view(area)),
+        cell_to_intdom(memory::make_const_view(cell_to_intdom))
     {
         arb_assert(cv_cap.size() == size());
         arb_assert(face_cond.size() == size());
@@ -115,15 +120,15 @@ struct matrix_state_flat {
 
     // Assemble the matrix
     // Afterwards the diagonal and RHS will have been set given dt, voltage and current.
-    //   dt_cell [ms] (per cell)
-    //   voltage [mV]
-    //   current [nA]
-    void assemble(const_view dt_cell, const_view voltage, const_view current) {
+    //   dt_intdom [ms] (per integration domain)
+    //   voltage   [mV]
+    //   current   [nA]
+    void assemble(const_view dt_intdom, const_view voltage, const_view current) {
         // perform assembly on the gpu
         assemble_matrix_flat(
             d.data(), rhs.data(), invariant_d.data(), voltage.data(),
             current.data(), cv_capacitance.data(), cv_area.data(),
-            cv_to_cell.data(), dt_cell.data(), size());
+            cv_to_cell.data(), dt_intdom.data(), cell_to_intdom.data(), size());
     }
 
     void solve() {
diff --git a/arbor/backends/gpu/matrix_state_interleaved.hpp b/arbor/backends/gpu/matrix_state_interleaved.hpp
index 2ac1989e..864b77eb 100644
--- a/arbor/backends/gpu/matrix_state_interleaved.hpp
+++ b/arbor/backends/gpu/matrix_state_interleaved.hpp
@@ -28,7 +28,8 @@ void assemble_matrix_interleaved(
     const fvm_index_type* sizes,
     const fvm_index_type* starts,
     const fvm_index_type* matrix_to_cell,
-    const fvm_value_type* dt_cell,
+    const fvm_value_type* dt_intdom,
+    const fvm_index_type* cell_to_intdom,
     unsigned padded_size, unsigned num_mtx);
 
 // host side wrapper for interleaved matrix solver kernel
@@ -122,6 +123,8 @@ struct matrix_state_interleaved {
     // the invariant part of the matrix diagonal
     array invariant_d;    // [μS]
 
+    iarray cell_to_intdom;
+
     // the length of a vector required to store values for one
     // matrix with padding
     unsigned padded_size;
@@ -144,7 +147,8 @@ struct matrix_state_interleaved {
                  const std::vector<index_type>& cell_cv_divs,
                  const std::vector<value_type>& cv_cap,
                  const std::vector<value_type>& face_cond,
-                 const std::vector<value_type>& area)
+                 const std::vector<value_type>& area,
+                 const std::vector<index_type>& cell_intdom)
     {
         arb_assert(cv_cap.size()    == p.size());
         arb_assert(face_cond.size() == p.size());
@@ -248,6 +252,7 @@ struct matrix_state_interleaved {
         matrix_sizes = memory::make_const_view(sizes_p);
         matrix_index = memory::make_const_view(cell_to_cv_p);
         matrix_to_cell_index = memory::make_const_view(perm);
+        cell_to_intdom = memory::make_const_view(cell_intdom);
 
         // Allocate space for storing the un-interleaved solution.
         solution_ = array(p.size());
@@ -259,16 +264,16 @@ struct matrix_state_interleaved {
 
     // Assemble the matrix
     // Afterwards the diagonal and RHS will have been set given dt, voltage and current.
-    //   dt_cell         [ms]     (per cell)
-    //   voltage         [mV]     (per compartment)
-    //   current density [A.m^-2] (per compartment)
-    void assemble(const_view dt_cell, const_view voltage, const_view current) {
+    //   dt_intdom         [ms]     (per integration domain)
+    //   voltage           [mV]     (per compartment)
+    //   current density   [A.m^-2] (per compartment)
+    void assemble(const_view dt_intdom, const_view voltage, const_view current) {
         assemble_matrix_interleaved
             (d.data(), rhs.data(), invariant_d.data(),
              voltage.data(), current.data(), cv_capacitance.data(), cv_area.data(),
              matrix_sizes.data(), matrix_index.data(),
              matrix_to_cell_index.data(),
-             dt_cell.data(), padded_matrix_size(), num_matrices());
+             dt_intdom.data(), cell_to_intdom.data(), padded_matrix_size(), num_matrices());
 
     }
 
diff --git a/arbor/backends/gpu/mechanism.cpp b/arbor/backends/gpu/mechanism.cpp
index 9a70b562..c7c629c4 100644
--- a/arbor/backends/gpu/mechanism.cpp
+++ b/arbor/backends/gpu/mechanism.cpp
@@ -68,7 +68,7 @@ void mechanism::instantiate(unsigned id,
 
     pp->width_ = width_;
 
-    pp->vec_ci_   = shared.cv_to_cell.data();
+    pp->vec_ci_   = shared.cv_to_intdom.data();
     pp->vec_t_    = shared.time.data();
     pp->vec_t_to_ = shared.time_to.data();
     pp->vec_dt_   = shared.dt_cv.data();
diff --git a/arbor/backends/gpu/shared_state.cpp b/arbor/backends/gpu/shared_state.cpp
index 5760b02c..fe499023 100644
--- a/arbor/backends/gpu/shared_state.cpp
+++ b/arbor/backends/gpu/shared_state.cpp
@@ -35,11 +35,9 @@ void update_time_to_impl(
     std::size_t n, fvm_value_type* time_to, const fvm_value_type* time,
     fvm_value_type dt, fvm_value_type tmax);
 
-void sync_time_to_impl(std::size_t n, fvm_value_type* time_to, const fvm_index_type* time_deps);
-
 void set_dt_impl(
-    fvm_size_type ncell, fvm_size_type ncomp, fvm_value_type* dt_cell, fvm_value_type* dt_comp,
-    const fvm_value_type* time_to, const fvm_value_type* time, const fvm_index_type* cv_to_cell);
+    fvm_size_type nintdom, fvm_size_type ncomp, fvm_value_type* dt_intdom, fvm_value_type* dt_comp,
+    const fvm_value_type* time_to, const fvm_value_type* time, const fvm_index_type* cv_to_intdom);
 
 void add_gj_current_impl(
     fvm_size_type n_gj, const fvm_gap_junction* gj, const fvm_value_type* v, fvm_value_type* i);
@@ -112,26 +110,24 @@ void ion_state::zero_current() {
 // Shared state methods:
 
 shared_state::shared_state(
-    fvm_size_type n_cell,
-    const std::vector<fvm_index_type>& cv_to_cell_vec,
-    const std::vector<fvm_index_type>& time_dep_vec,
+    fvm_size_type n_intdom,
+    const std::vector<fvm_index_type>& cv_to_intdom_vec,
     const std::vector<fvm_gap_junction>& gj_vec,
     unsigned // alignment parameter ignored.
 ):
-    n_cell(n_cell),
-    n_cv(cv_to_cell_vec.size()),
+    n_intdom(n_intdom),
+    n_cv(cv_to_intdom_vec.size()),
     n_gj(gj_vec.size()),
-    cv_to_cell(make_const_view(cv_to_cell_vec)),
-    time_dep(make_const_view(time_dep_vec)),
+    cv_to_intdom(make_const_view(cv_to_intdom_vec)),
     gap_junctions(make_const_view(gj_vec)),
-    time(n_cell),
-    time_to(n_cell),
-    dt_cell(n_cell),
+    time(n_intdom),
+    time_to(n_intdom),
+    dt_intdom(n_intdom),
     dt_cv(n_cv),
     voltage(n_cv),
     current_density(n_cv),
     temperature_degC(1),
-    deliverable_events(n_cell)
+    deliverable_events(n_intdom)
 {}
 
 void shared_state::add_ion(
@@ -177,15 +173,11 @@ void shared_state::ions_nernst_reversal_potential(fvm_value_type temperature_K)
 }
 
 void shared_state::update_time_to(fvm_value_type dt_step, fvm_value_type tmax) {
-    update_time_to_impl(n_cell, time_to.data(), time.data(), dt_step, tmax);
-}
-
-void shared_state::sync_time_to() {
-    sync_time_to_impl(n_cell, time_to.data(), time_dep.data());
+    update_time_to_impl(n_intdom, time_to.data(), time.data(), dt_step, tmax);
 }
 
 void shared_state::set_dt() {
-    set_dt_impl(n_cell, n_cv, dt_cell.data(), dt_cv.data(), time_to.data(), time.data(), cv_to_cell.data());
+    set_dt_impl(n_intdom, n_cv, dt_intdom.data(), dt_cv.data(), time_to.data(), time.data(), cv_to_intdom.data());
 }
 
 void shared_state::add_gj_current() {
@@ -193,7 +185,7 @@ void shared_state::add_gj_current() {
 }
 
 std::pair<fvm_value_type, fvm_value_type> shared_state::time_bounds() const {
-    return minmax_value_impl(n_cell, time.data());
+    return minmax_value_impl(n_intdom, time.data());
 }
 
 std::pair<fvm_value_type, fvm_value_type> shared_state::voltage_bounds() const {
@@ -206,10 +198,10 @@ void shared_state::take_samples(const sample_event_stream::state& s, array& samp
 
 // Debug interface
 std::ostream& operator<<(std::ostream& o, shared_state& s) {
-    o << " cv_to_cell " << s.cv_to_cell << "\n";
+    o << " cv_to_intdom " << s.cv_to_intdom << "\n";
     o << " time       " << s.time << "\n";
     o << " time_to    " << s.time_to << "\n";
-    o << " dt_cell    " << s.dt_cell << "\n";
+    o << " dt_intdom    " << s.dt_intdom << "\n";
     o << " dt_cv      " << s.dt_cv << "\n";
     o << " voltage    " << s.voltage << "\n";
     o << " current    " << s.current_density << "\n";
diff --git a/arbor/backends/gpu/shared_state.cu b/arbor/backends/gpu/shared_state.cu
index 806418b9..a5901cf5 100644
--- a/arbor/backends/gpu/shared_state.cu
+++ b/arbor/backends/gpu/shared_state.cu
@@ -34,24 +34,6 @@ void init_concentration_impl(unsigned n, T* Xi, T* Xo, const T* weight_Xi, const
     }
 }
 
-template <typename T, typename I>
-__global__ void sync_time_to_impl(unsigned n, T* time_to, const I* time_deps) {
-    unsigned i = threadIdx.x+blockIdx.x*blockDim.x;
-    if (i<n) {
-        if (time_deps[i] > 0) {
-            auto min_t = time_to[i];
-            for (int j = 1; j < time_deps[i]; j++) {
-                if (time_to[i+j] < min_t) {
-                    min_t = time_to[i+j];
-                }
-            }
-            for (int j = 0; j < time_deps[i]; j++) {
-                time_to[i+j] = min_t;
-            }
-        }
-    }
-}
-
 template <typename T>
 __global__ void update_time_to_impl(unsigned n, T* time_to, const T* time, T dt, T tmax) {
     unsigned i = threadIdx.x+blockIdx.x*blockDim.x;
@@ -131,15 +113,6 @@ void init_concentration_impl(
     kernel::init_concentration_impl<<<nblock, block_dim>>>(n, Xi, Xo, weight_Xi, weight_Xo, c_int, c_ext);
 }
 
-void sync_time_to_impl(std::size_t n, fvm_value_type* time_to, const fvm_index_type* time_deps)
-{
-    if (!n) return;
-
-    constexpr int block_dim = 128;
-    int nblock = block_count(n, block_dim);
-    kernel::sync_time_to_impl<<<nblock, block_dim>>>(n, time_to, time_deps);
-}
-
 void update_time_to_impl(
     std::size_t n, fvm_value_type* time_to, const fvm_value_type* time,
     fvm_value_type dt, fvm_value_type tmax)
@@ -152,17 +125,17 @@ void update_time_to_impl(
 }
 
 void set_dt_impl(
-    fvm_size_type ncell, fvm_size_type ncomp, fvm_value_type* dt_cell, fvm_value_type* dt_comp,
-    const fvm_value_type* time_to, const fvm_value_type* time, const fvm_index_type* cv_to_cell)
+    fvm_size_type nintdom, fvm_size_type ncomp, fvm_value_type* dt_intdom, fvm_value_type* dt_comp,
+    const fvm_value_type* time_to, const fvm_value_type* time, const fvm_index_type* cv_to_intdom)
 {
-    if (!ncell || !ncomp) return;
+    if (!nintdom || !ncomp) return;
 
     constexpr int block_dim = 128;
-    int nblock = block_count(ncell, block_dim);
-    kernel::vec_minus<<<nblock, block_dim>>>(ncell, dt_cell, time_to, time);
+    int nblock = block_count(nintdom, block_dim);
+    kernel::vec_minus<<<nblock, block_dim>>>(nintdom, dt_intdom, time_to, time);
 
     nblock = block_count(ncomp, block_dim);
-    kernel::gather<<<nblock, block_dim>>>(ncomp, dt_comp, dt_cell, cv_to_cell);
+    kernel::gather<<<nblock, block_dim>>>(ncomp, dt_comp, dt_intdom, cv_to_intdom);
 }
 
 void add_gj_current_impl(
diff --git a/arbor/backends/gpu/shared_state.hpp b/arbor/backends/gpu/shared_state.hpp
index c66ac3f1..778d82b2 100644
--- a/arbor/backends/gpu/shared_state.hpp
+++ b/arbor/backends/gpu/shared_state.hpp
@@ -65,16 +65,15 @@ struct ion_state {
 };
 
 struct shared_state {
-    fvm_size_type n_cell = 0; // Number of distinct cells (integration domains).
+    fvm_size_type n_intdom = 0; // Number of distinct integration domains.
     fvm_size_type n_cv = 0;   // Total number of CVs.
     fvm_size_type n_gj = 0;   // Total number of GJs.
 
-    iarray cv_to_cell;        // Maps CV index to cell index.
-    iarray time_dep; // Provides information about supercells
+    iarray cv_to_intdom;        // Maps CV index to intdom index.
     gjarray  gap_junctions;   // Stores gap_junction info.
-    array  time;              // Maps cell index to integration start time [ms].
-    array  time_to;           // Maps cell index to integration stop time [ms].
-    array  dt_cell;           // Maps cell index to (stop time) - (start time) [ms].
+    array  time;              // Maps intdom index to integration start time [ms].
+    array  time_to;           // Maps intdom index to integration stop time [ms].
+    array  dt_intdom;         // Maps intdom index to (stop time) - (start time) [ms].
     array  dt_cv;             // Maps CV index to dt [ms].
     array  voltage;           // Maps CV index to membrane voltage [mV].
     array  current_density;   // Maps CV index to current density [A/m²].
@@ -87,9 +86,8 @@ struct shared_state {
     shared_state() = default;
 
     shared_state(
-        fvm_size_type n_cell,
-        const std::vector<fvm_index_type>& cv_to_cell_vec,
-        const std::vector<fvm_index_type>& time_dep_vec,
+        fvm_size_type n_intdom,
+        const std::vector<fvm_index_type>& cv_to_intdom_vec,
         const std::vector<fvm_gap_junction>& gj_vec,
         unsigned align
     );
@@ -109,10 +107,7 @@ struct shared_state {
     // Set time_to to earliest of time+dt_step and tmax.
     void update_time_to(fvm_value_type dt_step, fvm_value_type tmax);
 
-    // Synchrnize the time_to for supercells.
-    void sync_time_to();
-
-    // Set the per-cell and per-compartment dt from time_to - time.
+    // Set the per-intdom and per-compartment dt from time_to - time.
     void set_dt();
 
     // Update gap_junction state
diff --git a/arbor/backends/gpu/threshold_watcher.cu b/arbor/backends/gpu/threshold_watcher.cu
index c18022ee..6377f009 100644
--- a/arbor/backends/gpu/threshold_watcher.cu
+++ b/arbor/backends/gpu/threshold_watcher.cu
@@ -30,7 +30,7 @@ inline T lerp(T a, T b, T u) {
 __global__
 void test_thresholds_impl(
     int size,
-    const fvm_index_type* cv_to_cell, const fvm_value_type* t_after, const fvm_value_type* t_before,
+    const fvm_index_type* cv_to_intdom, const fvm_value_type* t_after, const fvm_value_type* t_before,
     stack_storage<threshold_crossing>& stack,
     fvm_index_type* is_crossed, fvm_value_type* prev_values,
     const fvm_index_type* cv_index, const fvm_value_type* values, const fvm_value_type* thresholds)
@@ -43,7 +43,7 @@ void test_thresholds_impl(
     if (i<size) {
         // Test for threshold crossing
         const auto cv     = cv_index[i];
-        const auto cell   = cv_to_cell[cv];
+        const auto cell   = cv_to_intdom[cv];
         const auto v_prev = prev_values[i];
         const auto v      = values[cv];
         const auto thresh = thresholds[i];
@@ -86,7 +86,7 @@ extern void reset_crossed_impl(
 
 void test_thresholds_impl(
     int size,
-    const fvm_index_type* cv_to_cell, const fvm_value_type* t_after, const fvm_value_type* t_before,
+    const fvm_index_type* cv_to_intdom, const fvm_value_type* t_after, const fvm_value_type* t_before,
     stack_storage<threshold_crossing>& stack,
     fvm_index_type* is_crossed, fvm_value_type* prev_values,
     const fvm_index_type* cv_index, const fvm_value_type* values, const fvm_value_type* thresholds)
@@ -95,7 +95,7 @@ void test_thresholds_impl(
         constexpr int block_dim = 128;
         const int grid_dim = impl::block_count(size, block_dim);
         kernel::test_thresholds_impl<<<grid_dim, block_dim>>>(
-            size, cv_to_cell, t_after, t_before, stack, is_crossed, prev_values, cv_index, values, thresholds);
+            size, cv_to_intdom, t_after, t_before, stack, is_crossed, prev_values, cv_index, values, thresholds);
     }
 }
 
diff --git a/arbor/backends/gpu/threshold_watcher.hpp b/arbor/backends/gpu/threshold_watcher.hpp
index e0668a9f..7b1ad17b 100644
--- a/arbor/backends/gpu/threshold_watcher.hpp
+++ b/arbor/backends/gpu/threshold_watcher.hpp
@@ -22,7 +22,7 @@ namespace gpu {
 
 void test_thresholds_impl(
     int size,
-    const fvm_index_type* cv_to_cell, const fvm_value_type* t_after, const fvm_value_type* t_before,
+    const fvm_index_type* cv_to_intdom, const fvm_value_type* t_after, const fvm_value_type* t_before,
     stack_storage<threshold_crossing>& stack,
     fvm_index_type* is_crossed, fvm_value_type* prev_values,
     const fvm_index_type* cv_index, const fvm_value_type* values, const fvm_value_type* thresholds);
@@ -44,7 +44,7 @@ public:
     threshold_watcher(const execution_context& ctx): stack_(ctx.gpu) {}
 
     threshold_watcher(
-        const fvm_index_type* cv_to_cell,
+        const fvm_index_type* cv_to_intdom,
         const fvm_value_type* t_before,
         const fvm_value_type* t_after,
         const fvm_value_type* values,
@@ -52,7 +52,7 @@ public:
         const std::vector<fvm_value_type>& thresholds,
         const execution_context& ctx
     ):
-        cv_to_cell_(cv_to_cell),
+        cv_to_intdom_(cv_to_intdom),
         t_before_(t_before),
         t_after_(t_after),
         values_(values),
@@ -109,7 +109,7 @@ public:
         if (size()>0) {
             test_thresholds_impl(
                 (int)size(),
-                cv_to_cell_, t_after_, t_before_,
+                cv_to_intdom_, t_after_, t_before_,
                 stack_.storage(),
                 is_crossed_.data(), v_prev_.data(),
                 cv_index_.data(), values_, thresholds_.data());
@@ -129,7 +129,7 @@ public:
 private:
     /// Non-owning pointers to gpu-side cv-to-cell map, per-cell time data,
     /// and the values for to test against thresholds.
-    const fvm_index_type* cv_to_cell_ = nullptr;
+    const fvm_index_type* cv_to_intdom_ = nullptr;
     const fvm_value_type* t_before_ = nullptr;
     const fvm_value_type* t_after_ = nullptr;
     const fvm_value_type* values_ = nullptr;
diff --git a/arbor/backends/multicore/fvm.hpp b/arbor/backends/multicore/fvm.hpp
index 1cbae3ef..1f9c019d 100644
--- a/arbor/backends/multicore/fvm.hpp
+++ b/arbor/backends/multicore/fvm.hpp
@@ -51,7 +51,7 @@ struct backend {
         const execution_context& context)
     {
         return threshold_watcher(
-            state.cv_to_cell.data(),
+            state.cv_to_intdom.data(),
             state.time.data(),
             state.time_to.data(),
             state.voltage.data(),
diff --git a/arbor/backends/multicore/matrix_state.hpp b/arbor/backends/multicore/matrix_state.hpp
index a4d318aa..9e54b8eb 100644
--- a/arbor/backends/multicore/matrix_state.hpp
+++ b/arbor/backends/multicore/matrix_state.hpp
@@ -29,6 +29,8 @@ public:
     array face_conductance;    // [μS]
     array cv_area;             // [μm^2]
 
+    iarray cell_to_intdom;
+
     // the invariant part of the matrix diagonal
     array invariant_d;         // [μS]
 
@@ -38,13 +40,15 @@ public:
                  const std::vector<index_type>& cell_cv_divs,
                  const std::vector<value_type>& cap,
                  const std::vector<value_type>& cond,
-                 const std::vector<value_type>& area):
+                 const std::vector<value_type>& area,
+                 const std::vector<index_type>& cell_to_intdom):
         parent_index(p.begin(), p.end()),
         cell_cv_divs(cell_cv_divs.begin(), cell_cv_divs.end()),
         d(size(), 0), u(size(), 0), rhs(size()),
         cv_capacitance(cap.begin(), cap.end()),
         face_conductance(cond.begin(), cond.end()),
-        cv_area(area.begin(), area.end())
+        cv_area(area.begin(), area.end()),
+        cell_to_intdom(cell_to_intdom.begin(), cell_to_intdom.end())
     {
         arb_assert(cap.size() == size());
         arb_assert(cond.size() == size());
@@ -70,16 +74,16 @@ public:
 
     // Assemble the matrix
     // Afterwards the diagonal and RHS will have been set given dt, voltage and current.
-    //   dt_cell         [ms]     (per cell)
+    //   dt_intdom       [ms]     (per integration domain)
     //   voltage         [mV]     (per compartment)
     //   current density [A.m^-2] (per compartment)
-    void assemble(const_view dt_cell, const_view voltage, const_view current) {
+    void assemble(const_view dt_intdom, const_view voltage, const_view current) {
         auto cell_cv_part = util::partition_view(cell_cv_divs);
         const index_type ncells = cell_cv_part.size();
 
         // loop over submatrices
         for (auto m: util::make_span(0, ncells)) {
-            auto dt = dt_cell[m];
+            auto dt = dt_intdom[cell_to_intdom[m]];
 
             if (dt>0) {
                 value_type factor = 1e-3/dt;
diff --git a/arbor/backends/multicore/mechanism.cpp b/arbor/backends/multicore/mechanism.cpp
index 63bd3449..5dc5c618 100644
--- a/arbor/backends/multicore/mechanism.cpp
+++ b/arbor/backends/multicore/mechanism.cpp
@@ -70,7 +70,7 @@ void mechanism::instantiate(unsigned id, backend::shared_state& shared, const la
 
     // Assign non-owning views onto shared state:
 
-    vec_ci_   = shared.cv_to_cell.data();
+    vec_ci_   = shared.cv_to_intdom.data();
     vec_t_    = shared.time.data();
     vec_t_to_ = shared.time_to.data();
     vec_dt_   = shared.dt_cv.data();
diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp
index 3bb73441..6313d429 100644
--- a/arbor/backends/multicore/shared_state.cpp
+++ b/arbor/backends/multicore/shared_state.cpp
@@ -115,35 +115,31 @@ void ion_state::zero_current() {
 // shared_state methods:
 
 shared_state::shared_state(
-    fvm_size_type n_cell,
-    const std::vector<fvm_index_type>& cv_to_cell_vec,
-    const std::vector<fvm_index_type>& time_dep_vec,
+    fvm_size_type n_intdom,
+    const std::vector<fvm_index_type>& cv_to_intdom_vec,
     const std::vector<fvm_gap_junction>& gj_vec,
     unsigned align
 ):
     alignment(min_alignment(align)),
     alloc(alignment),
-    n_cell(n_cell),
-    n_cv(cv_to_cell_vec.size()),
+    n_intdom(n_intdom),
+    n_cv(cv_to_intdom_vec.size()),
     n_gj(gj_vec.size()),
-    cv_to_cell(math::round_up(n_cv, alignment), pad(alignment)),
-    time_dep(n_cell),
+    cv_to_intdom(math::round_up(n_cv, alignment), pad(alignment)),
     gap_junctions(math::round_up(n_gj, alignment), pad(alignment)),
-    time(n_cell, pad(alignment)),
-    time_to(n_cell, pad(alignment)),
-    dt_cell(n_cell, pad(alignment)),
+    time(n_intdom, pad(alignment)),
+    time_to(n_intdom, pad(alignment)),
+    dt_intdom(n_intdom, pad(alignment)),
     dt_cv(n_cv, pad(alignment)),
     voltage(n_cv, pad(alignment)),
     current_density(n_cv, pad(alignment)),
     temperature_degC(NAN),
-    deliverable_events(n_cell)
+    deliverable_events(n_intdom)
 {
-    std::copy(time_dep_vec.begin(), time_dep_vec.end(), time_dep.begin());
-
-    // For indices in the padded tail of cv_to_cell, set index to last valid cell index.
+    // For indices in the padded tail of cv_to_intdom, set index to last valid intdom index.
     if (n_cv>0) {
-        std::copy(cv_to_cell_vec.begin(), cv_to_cell_vec.end(), cv_to_cell.begin());
-        std::fill(cv_to_cell.begin() + n_cv, cv_to_cell.end(), cv_to_cell_vec.back());
+        std::copy(cv_to_intdom_vec.begin(), cv_to_intdom_vec.end(), cv_to_intdom.begin());
+        std::fill(cv_to_intdom.begin() + n_cv, cv_to_intdom.end(), cv_to_intdom_vec.back());
     }
     if (n_gj>0) {
         std::copy(gj_vec.begin(), gj_vec.end(), gap_junctions.begin());
@@ -192,24 +188,9 @@ void shared_state::ions_nernst_reversal_potential(fvm_value_type temperature_K)
         i.second.nernst(temperature_K);
     }
 }
-void shared_state::sync_time_to() {
-    for (fvm_size_type i = 0; i<n_cell; i++) {
-        if (!time_dep[i]) continue;
-
-        fvm_value_type min_t = time_to[i];
-        for (int j = 1; j < time_dep[i]; j++) {
-            if (time_to[i+j] < min_t) {
-                min_t = time_to[i+j];
-            }
-        }
-        for (int j = 0; j < time_dep[i]; j++) {
-            time_to[i+j] = min_t;
-        }
-    }
-}
 
 void shared_state::update_time_to(fvm_value_type dt_step, fvm_value_type tmax) {
-    for (fvm_size_type i = 0; i<n_cell; i+=simd_width) {
+    for (fvm_size_type i = 0; i<n_intdom; i+=simd_width) {
         simd_value_type t(time.data()+i);
         t = min(t+dt_step, simd_value_type(tmax));
         t.copy_to(time_to.data()+i);
@@ -217,18 +198,18 @@ void shared_state::update_time_to(fvm_value_type dt_step, fvm_value_type tmax) {
 }
 
 void shared_state::set_dt() {
-    for (fvm_size_type j = 0; j<n_cell; j+=simd_width) {
+    for (fvm_size_type j = 0; j<n_intdom; j+=simd_width) {
         simd_value_type t(time.data()+j);
         simd_value_type t_to(time_to.data()+j);
 
         auto dt = t_to-t;
-        dt.copy_to(dt_cell.data()+j);
+        dt.copy_to(dt_intdom.data()+j);
     }
 
     for (fvm_size_type i = 0; i<n_cv; i+=simd_width) {
-        simd_index_type cell_idx(cv_to_cell.data()+i);
+        simd_index_type intdom_idx(cv_to_intdom.data()+i);
 
-        simd_value_type dt(simd::indirect(dt_cell.data(), cell_idx));
+        simd_value_type dt(simd::indirect(dt_intdom.data(), intdom_idx));
         dt.copy_to(dt_cv.data()+i);
     }
 }
@@ -272,12 +253,12 @@ void shared_state::take_samples(
 std::ostream& operator<<(std::ostream& out, const shared_state& s) {
     using io::csv;
 
-    out << "n_cell     " << s.n_cell << "\n";
+    out << "n_intdom     " << s.n_intdom << "\n";
     out << "n_cv       " << s.n_cv << "\n";
-    out << "cv_to_cell " << csv(s.cv_to_cell) << "\n";
+    out << "cv_to_intdom " << csv(s.cv_to_intdom) << "\n";
     out << "time       " << csv(s.time) << "\n";
     out << "time_to    " << csv(s.time_to) << "\n";
-    out << "dt_cell    " << csv(s.dt_cell) << "\n";
+    out << "dt_intdom    " << csv(s.dt_intdom) << "\n";
     out << "dt_cv      " << csv(s.dt_cv) << "\n";
     out << "voltage    " << csv(s.voltage) << "\n";
     out << "current    " << csv(s.current_density) << "\n";
diff --git a/arbor/backends/multicore/shared_state.hpp b/arbor/backends/multicore/shared_state.hpp
index 40be9eb2..fc2c0e7f 100644
--- a/arbor/backends/multicore/shared_state.hpp
+++ b/arbor/backends/multicore/shared_state.hpp
@@ -84,16 +84,15 @@ struct shared_state {
     unsigned alignment = 1;   // Alignment and padding multiple.
     util::padded_allocator<> alloc;  // Allocator with corresponging alignment/padding.
 
-    fvm_size_type n_cell = 0; // Number of distinct cells (integration domains).
+    fvm_size_type n_intdom = 0; // Number of integration domains.
     fvm_size_type n_cv = 0;   // Total number of CVs.
     fvm_size_type n_gj = 0;   // Total number of GJs.
 
-    iarray cv_to_cell;        // Maps CV index to cell index.
-    iarray time_dep;          // Provides information about supercells
+    iarray cv_to_intdom;        // Maps CV index to integration domain index.
     gjarray  gap_junctions;   // Stores gap_junction info.
-    array  time;              // Maps cell index to integration start time [ms].
-    array  time_to;           // Maps cell index to integration stop time [ms].
-    array  dt_cell;           // Maps cell index to (stop time) - (start time) [ms].
+    array  time;              // Maps intdom index to integration start time [ms].
+    array  time_to;           // Maps intdom index to integration stop time [ms].
+    array  dt_intdom;           // Maps  index to (stop time) - (start time) [ms].
     array  dt_cv;             // Maps CV index to dt [ms].
     array  voltage;           // Maps CV index to membrane voltage [mV].
     array  current_density;   // Maps CV index to current density [A/m²].
@@ -106,9 +105,8 @@ struct shared_state {
     shared_state() = default;
 
     shared_state(
-        fvm_size_type n_cell,
-        const std::vector<fvm_index_type>& cv_to_cell_vec,
-        const std::vector<fvm_index_type>& time_dep_vec,
+        fvm_size_type n_intdom,
+        const std::vector<fvm_index_type>& cv_to_intdom_vec,
         const std::vector<fvm_gap_junction>& gj_vec,
         unsigned align
     );
@@ -128,10 +126,7 @@ struct shared_state {
     // Set time_to to earliest of time+dt_step and tmax.
     void update_time_to(fvm_value_type dt_step, fvm_value_type tmax);
 
-    // Synchrnize the time_to for supercells.
-    void sync_time_to();
-
-    // Set the per-cell and per-compartment dt from time_to - time.
+    // Set the per-integration domain and per-compartment dt from time_to - time.
     void set_dt();
 
     // Update gap_junction state
diff --git a/arbor/backends/multicore/threshold_watcher.hpp b/arbor/backends/multicore/threshold_watcher.hpp
index 68b1d61d..e356f486 100644
--- a/arbor/backends/multicore/threshold_watcher.hpp
+++ b/arbor/backends/multicore/threshold_watcher.hpp
@@ -18,7 +18,7 @@ public:
     threshold_watcher(const execution_context& ctx) {}
 
     threshold_watcher(
-        const fvm_index_type* cv_to_cell,
+        const fvm_index_type* cv_to_intdom,
         const fvm_value_type* t_before,
         const fvm_value_type* t_after,
         const fvm_value_type* values,
@@ -26,7 +26,7 @@ public:
         const std::vector<fvm_value_type>& thresholds,
         const execution_context& context
     ):
-        cv_to_cell_(cv_to_cell),
+        cv_to_intdom_(cv_to_intdom),
         t_before_(t_before),
         t_after_(t_after),
         values_(values),
@@ -66,7 +66,7 @@ public:
     void test() {
         for (fvm_size_type i = 0; i<n_cv_; ++i) {
             auto cv     = cv_index_[i];
-            auto cell   = cv_to_cell_[cv];
+            auto cell   = cv_to_intdom_[cv];
             auto v_prev = v_prev_[i];
             auto v      = values_[cv];
             auto thresh = thresholds_[i];
@@ -104,7 +104,7 @@ public:
 private:
     /// Non-owning pointers to cv-to-cell map, per-cell time data,
     /// and the values for to test against thresholds.
-    const fvm_index_type* cv_to_cell_ = nullptr;
+    const fvm_index_type* cv_to_intdom_ = nullptr;
     const fvm_value_type* t_before_ = nullptr;
     const fvm_value_type* t_after_ = nullptr;
     const fvm_value_type* values_ = nullptr;
diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp
index 9d2d1607..24c69019 100644
--- a/arbor/fvm_layout.cpp
+++ b/arbor/fvm_layout.cpp
@@ -111,6 +111,7 @@ namespace {
 //
 
 fvm_discretization fvm_discretize(const std::vector<mc_cell>& cells) {
+
     using value_type = fvm_value_type;
     using index_type = fvm_index_type;
     using size_type = fvm_size_type;
diff --git a/arbor/fvm_lowered_cell.hpp b/arbor/fvm_lowered_cell.hpp
index e49c610b..5f77ca1e 100644
--- a/arbor/fvm_lowered_cell.hpp
+++ b/arbor/fvm_lowered_cell.hpp
@@ -28,8 +28,8 @@ struct fvm_lowered_cell {
 
     virtual void initialize(
         const std::vector<cell_gid_type>& gids,
-        const std::vector<int>& deps,
         const recipe& rec,
+        std::vector<fvm_index_type>& cell_to_intdom,
         std::vector<target_handle>& target_handles,
         probe_association_map<probe_handle>& probe_map) = 0;
 
diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp
index 31edf2bf..70c13263 100644
--- a/arbor/fvm_lowered_cell_impl.hpp
+++ b/arbor/fvm_lowered_cell_impl.hpp
@@ -10,9 +10,11 @@
 #include <cmath>
 #include <iterator>
 #include <memory>
+#include <queue>
 #include <stdexcept>
 #include <utility>
 #include <vector>
+#include <unordered_set>
 
 #include <arbor/assert.hpp>
 #include <arbor/common_types.hpp>
@@ -50,8 +52,8 @@ public:
 
     void initialize(
         const std::vector<cell_gid_type>& gids,
-        const std::vector<int>& deps,
         const recipe& rec,
+        std::vector<fvm_index_type>& cell_to_intdom,
         std::vector<target_handle>& target_handles,
         probe_association_map<probe_handle>& probe_map) override;
 
@@ -67,6 +69,13 @@ public:
         const recipe& rec,
         const fvm_discretization& D);
 
+    // Generates indom index for every gid, guarantees that gids belonging to the same supercell are in the same intdom
+    // Fills cell_to_intdom map; returns number of intdoms
+    fvm_size_type fvm_intdom(
+        const recipe& rec,
+        const std::vector<cell_gid_type>& gids,
+        std::vector<fvm_index_type>& cell_to_intdom);
+
     value_type time() const override { return tmin_; }
 
     //Exposed for testing purposes
@@ -215,7 +224,6 @@ fvm_integration_result fvm_lowered_cell_impl<Backend>::integrate(
 
         state_->update_time_to(dt_max, tfinal);
         state_->deliverable_events.event_time_if_before(state_->time_to);
-        state_->sync_time_to();
         state_->set_dt();
         PL();
 
@@ -230,7 +238,7 @@ fvm_integration_result fvm_lowered_cell_impl<Backend>::integrate(
         // Integrate voltage by matrix solve.
 
         PE(advance_integrate_matrix_build);
-        matrix_.assemble(state_->dt_cell, state_->voltage, state_->current_density);
+        matrix_.assemble(state_->dt_intdom, state_->voltage, state_->current_density);
         PL();
         PE(advance_integrate_matrix_solve);
         matrix_.solve();
@@ -312,8 +320,8 @@ void fvm_lowered_cell_impl<B>::assert_voltage_bounded(fvm_value_type bound) {
 template <typename B>
 void fvm_lowered_cell_impl<B>::initialize(
     const std::vector<cell_gid_type>& gids,
-    const std::vector<int>& deps,
     const recipe& rec,
+    std::vector<fvm_index_type>& cell_to_intdom,
     std::vector<target_handle>& target_handles,
     probe_association_map<probe_handle>& probe_map)
 {
@@ -363,12 +371,19 @@ void fvm_lowered_cell_impl<B>::initialize(
 
     check_voltage_mV = global_props.membrane_voltage_limit_mV;
 
+    auto num_intdoms = fvm_intdom(rec, gids, cell_to_intdom);
+
     // Discretize cells, build matrix.
 
     fvm_discretization D = fvm_discretize(cells);
+
+    std::vector<index_type> cv_to_intdom(D.ncomp);
+    std::transform(D.cv_to_cell.begin(), D.cv_to_cell.end(), cv_to_intdom.begin(),
+                   [&cell_to_intdom](index_type i){ return cell_to_intdom[i]; });
+
     arb_assert(D.ncell == ncell);
-    matrix_ = matrix<backend>(D.parent_cv, D.cell_cv_bounds, D.cv_capacitance, D.face_conductance, D.cv_area);
-    sample_events_ = sample_event_stream(ncell);
+    matrix_ = matrix<backend>(D.parent_cv, D.cell_cv_bounds, D.cv_capacitance, D.face_conductance, D.cv_area, cell_to_intdom);
+    sample_events_ = sample_event_stream(num_intdoms);
 
     // Discretize mechanism data.
 
@@ -385,7 +400,7 @@ void fvm_lowered_cell_impl<B>::initialize(
         util::transform_view(keys(mech_data.mechanisms),
             [&](const std::string& name) { return mech_instance(name)->data_alignment(); }));
 
-    state_ = std::make_unique<shared_state>(ncell, D.cv_to_cell, deps, gj_vector, data_alignment? data_alignment: 1u);
+    state_ = std::make_unique<shared_state>(num_intdoms, cv_to_intdom, gj_vector, data_alignment? data_alignment: 1u);
 
     // Instantiate mechanisms and ions.
 
@@ -422,7 +437,7 @@ void fvm_lowered_cell_impl<B>::initialize(
 
                 // (builtin stimulus, for example, has no targets)
                 if (!config.target.empty()) {
-                    target_handles[config.target[i]] = target_handle(mech_id, i, D.cv_to_cell[cv]);
+                    target_handles[config.target[i]] = target_handle(mech_id, i, cv_to_intdom[cv]);
                 }
             }
         }
@@ -531,4 +546,54 @@ std::vector<fvm_gap_junction> fvm_lowered_cell_impl<B>::fvm_gap_junctions(
     return v;
 }
 
+template <typename B>
+fvm_size_type fvm_lowered_cell_impl<B>::fvm_intdom(
+        const recipe& rec,
+        const std::vector<cell_gid_type>& gids,
+        std::vector<fvm_index_type>& cell_to_intdom) {
+
+    cell_to_intdom.resize(gids.size());
+
+    std::unordered_map<cell_gid_type, cell_size_type> gid_to_loc;
+    for (auto i: util::count_along(gids)) {
+        gid_to_loc[gids[i]] = i;
+    }
+
+    std::unordered_set<cell_gid_type> visited;
+    std::queue<cell_gid_type> intdomq;
+    cell_size_type intdom_id = 0;
+
+    for (auto gid: gids) {
+        if (visited.count(gid)) continue;
+        visited.insert(gid);
+
+        intdomq.push(gid);
+        while (!intdomq.empty()) {
+            auto g = intdomq.front();
+            intdomq.pop();
+
+            cell_to_intdom[gid_to_loc[g]] = intdom_id;
+
+            for (auto gj: rec.gap_junctions_on(g)) {
+                cell_gid_type peer =
+                        gj.local.gid==g? gj.peer.gid:
+                        gj.peer.gid==g?  gj.local.gid:
+                        throw bad_cell_description(cell_kind::cable1d_neuron, g);
+
+                if (!gid_to_loc.count(peer)) {
+                    throw gj_unsupported_domain_decomposition(g, peer);
+                }
+
+                if (!visited.count(peer)) {
+                    visited.insert(peer);
+                    intdomq.push(peer);
+                }
+            }
+        }
+        intdom_id++;
+    }
+
+    return intdom_id;
+}
+
 } // namespace arb
diff --git a/arbor/matrix.hpp b/arbor/matrix.hpp
index bab4c6ef..94958c00 100644
--- a/arbor/matrix.hpp
+++ b/arbor/matrix.hpp
@@ -35,10 +35,12 @@ public:
            const std::vector<index_type>& ci,
            const std::vector<value_type>& cv_capacitance,
            const std::vector<value_type>& face_conductance,
-           const std::vector<value_type>& cv_area):
+           const std::vector<value_type>& cv_area,
+           const std::vector<index_type>& cell_to_intdom):
         parent_index_(pi.begin(), pi.end()),
         cell_index_(ci.begin(), ci.end()),
-        state_(pi, ci, cv_capacitance, face_conductance, cv_area)
+        cell_to_intdom_(cell_to_intdom.begin(), cell_to_intdom.end()),
+        state_(pi, ci, cv_capacitance, face_conductance, cv_area, cell_to_intdom)
     {
         arb_assert(cell_index_[num_cells()] == index_type(parent_index_.size()));
     }
@@ -81,6 +83,9 @@ private:
     /// indexes that point to the start of each cell in the index
     iarray cell_index_;
 
+    /// indexes that point to the index of the integration domain
+    iarray cell_to_intdom_;
+
 public:
     // Provide via public interface to make testing much easier.
     // If you modify this directly without knowing what you are doing,
diff --git a/arbor/mc_cell_group.cpp b/arbor/mc_cell_group.cpp
index ad6673ff..ec8371ab 100644
--- a/arbor/mc_cell_group.cpp
+++ b/arbor/mc_cell_group.cpp
@@ -24,10 +24,12 @@
 
 namespace arb {
 
+ARB_DEFINE_LEXICOGRAPHIC_ORDERING(arb::target_handle,(a.mech_id,a.mech_index,a.intdom_index),(b.mech_id,b.mech_index,b.intdom_index))
+ARB_DEFINE_LEXICOGRAPHIC_ORDERING(arb::deliverable_event,(a.time,a.handle,a.weight),(b.time,b.handle,b.weight))
+
 mc_cell_group::mc_cell_group(const std::vector<cell_gid_type>& gids, const recipe& rec, fvm_lowered_cell_ptr lowered):
-    lowered_(std::move(lowered))
+    gids_(gids), lowered_(std::move(lowered))
 {
-    generate_deps_gids(rec, gids);
     // Default to no binning of events
     set_binning_policy(binning_kind::none, 0);
 
@@ -47,7 +49,7 @@ mc_cell_group::mc_cell_group(const std::vector<cell_gid_type>& gids, const recip
     target_handles_.reserve(n_targets);
 
     // Construct cell implementation, retrieving handles and maps. 
-    lowered_->initialize(gids_, deps_, rec, target_handles_, probe_map_);
+    lowered_->initialize(gids_, rec, cell_to_intdom_, target_handles_, probe_map_);
 
     // Create a list of the global identifiers for the spike sources
     for (auto source_gid: gids_) {
@@ -58,58 +60,6 @@ mc_cell_group::mc_cell_group(const std::vector<cell_gid_type>& gids, const recip
     spike_sources_.shrink_to_fit();
 }
 
-// Fills gids_ and deps_: gids_ are sorted such that members of the same supercell are consecutive
-void mc_cell_group::generate_deps_gids(const recipe& rec, std::vector<cell_gid_type> gids) {
-
-    std::unordered_map<cell_gid_type, cell_size_type> gid_to_loc;
-    for (auto i: util::count_along(gids)) {
-        gid_to_loc[gids[i]] = i;
-    }
-
-    deps_.reserve(gids_.size());
-    std::unordered_set<cell_gid_type> visited;
-    std::queue<cell_gid_type> scq;
-
-    for (auto gid: gids) {
-        if (visited.count(gid)) continue;
-        visited.insert(gid);
-
-        cell_size_type sc_size = 0;
-        scq.push(gid);
-        while (!scq.empty()) {
-            auto g = scq.front();
-            scq.pop();
-
-            gids_.push_back(g);
-            ++sc_size;
-
-            for (auto gj: rec.gap_junctions_on(g)) {
-                cell_gid_type peer =
-                        gj.local.gid==g? gj.peer.gid:
-                        gj.peer.gid==g?  gj.local.gid:
-                        throw bad_cell_description(cell_kind::cable1d_neuron, g);
-
-                if (!gid_to_loc.count(peer)) {
-                    throw gj_unsupported_domain_decomposition(g, peer);
-                }
-
-                if (!visited.count(peer)) {
-                    visited.insert(peer);
-                    scq.push(peer);
-                }
-            }
-        }
-
-        deps_.push_back(sc_size>1? sc_size: 0);
-        deps_.insert(deps_.end(), sc_size-1, 0);
-    }
-
-    perm_gids_.reserve(gids_.size());
-    for (auto gid: gids_) {
-        perm_gids_.push_back(gid_to_loc[gid]);
-    }
-}
-
 void mc_cell_group::reset() {
     spikes_.clear();
 
@@ -135,21 +85,51 @@ void mc_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& e
 
     PE(advance_eventsetup);
     staged_events_.clear();
+
+    fvm_index_type ev_begin = 0, ev_mid = 0, ev_end = 0;
     // skip event binning if empty lanes are passed
     if (event_lanes.size()) {
-        for (auto lid: util::count_along(gids_)) {
-            auto& lane = event_lanes[perm_gids_[lid]];
+
+        std::vector<cell_size_type> idx_sorted_by_intdom(cell_to_intdom_.size());
+        std::iota(idx_sorted_by_intdom.begin(), idx_sorted_by_intdom.end(), 0);
+        util::sort_by(idx_sorted_by_intdom, [&](cell_size_type i) { return cell_to_intdom_[i]; });
+
+        /// Event merging on integration domain could benefit from the use of the logic from `tree_merge_events`
+        fvm_index_type prev_intdom = -1;
+        for (auto i: util::count_along(gids_)) {
+            unsigned count_staged = 0;
+
+            auto lid = idx_sorted_by_intdom[i];
+            auto& lane = event_lanes[lid];
+            auto curr_intdom = cell_to_intdom_[lid];
+
             for (auto e: lane) {
                 if (e.time>=ep.tfinal) break;
                 e.time = binners_[lid].bin(e.time, tstart);
                 auto h = target_handles_[target_handle_divisions_[lid]+e.target.index];
                 auto ev = deliverable_event(e.time, h, e.weight);
                 staged_events_.push_back(ev);
+                count_staged++;
             }
+
+            ev_end += count_staged;
+
+            if (curr_intdom != prev_intdom) {
+                ev_begin = ev_end - count_staged;
+                prev_intdom = curr_intdom;
+            }
+            else {
+                std::inplace_merge(staged_events_.begin() + ev_begin,
+                                   staged_events_.begin() + ev_mid,
+                                   staged_events_.begin() + ev_end);
+            }
+
+            ev_mid = ev_end;
         }
     }
     PL();
 
+
     // Create sample events and delivery information.
     //
     // For each (schedule, sampler, probe set) in the sampler association
@@ -196,7 +176,7 @@ void mc_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& e
             call_info.push_back({sa.sampler, pid, p.tag, n_samples, n_samples+n_times});
 
             for (auto t: sample_times) {
-                sample_event ev{t, cell_index, {p.handle, n_samples++}};
+                sample_event ev{t, (cell_gid_type)cell_to_intdom_[cell_index], {p.handle, n_samples++}};
                 sample_events.push_back(ev);
             }
         }
diff --git a/arbor/mc_cell_group.hpp b/arbor/mc_cell_group.hpp
index 4efd99d0..f22d3b22 100644
--- a/arbor/mc_cell_group.hpp
+++ b/arbor/mc_cell_group.hpp
@@ -57,26 +57,12 @@ public:
 
     void remove_all_samplers() override;
 
-    void generate_deps_gids(const recipe& rec, std::vector<cell_gid_type>);
-
-    std::vector<cell_gid_type> get_gids() {
-        return gids_;
-    }
-
-    std::vector<int> get_dependencies() {
-        return deps_;
-    }
-
 private:
     // List of the gids of the cells in the group.
     std::vector<cell_gid_type> gids_;
 
-    // Permutation of the gids of the cells in the group.
-    // perm_gids_[i] is the index of gids_[i] in the original gids vector passed to the ctor
-    std::vector<cell_size_type> perm_gids_;
-
-    // List of the dependencies of the cells in the group.
-    std::vector<int> deps_;
+    // Map from gid to integration domain id
+    std::vector<fvm_index_type> cell_to_intdom_;
 
     // Hash table for converting gid to local index
     std::unordered_map<cell_gid_type, cell_gid_type> gid_index_map_;
diff --git a/example/gap_junctions/gap_junctions.cpp b/example/gap_junctions/gap_junctions.cpp
index 94a697f7..e37bb671 100644
--- a/example/gap_junctions/gap_junctions.cpp
+++ b/example/gap_junctions/gap_junctions.cpp
@@ -21,6 +21,9 @@
 #include <arbor/recipe.hpp>
 #include <arbor/version.hpp>
 
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 
@@ -43,18 +46,18 @@ using arb::cell_probe_address;
 void write_trace_json(const std::vector<arb::trace_data<double>>& trace, unsigned rank);
 
 // Generate a cell.
-arb::mc_cell gj_cell(double delay, double duration);
+arb::mc_cell gj_cell(cell_gid_type gid, unsigned ncells, double stim_duration);
 
 class gj_recipe: public arb::recipe {
 public:
     gj_recipe(gap_params params): params_(params) {}
 
     cell_size_type num_cells() const override {
-        return params_.cells_per_ring * params_.num_rings;
+        return params_.n_cells_per_cable * params_.n_cables;
     }
 
     arb::util::unique_any get_cell_description(cell_gid_type gid) const override {
-        return gj_cell(params_.delay*gid, params_.duration);
+        return gj_cell(gid, params_.n_cells_per_cable, params_.stim_duration);
     }
 
     cell_kind get_cell_kind(cell_gid_type gid) const override {
@@ -66,9 +69,16 @@ public:
         return 1;
     }
 
-    // The cell has one target synapse, which will be connected to cell gid-1.
+    // The cell has one target synapse, which will be connected to a cell in another cable.
     cell_size_type num_targets(cell_gid_type gid) const override {
-        return 0;
+        return 1;
+    }
+
+    std::vector<arb::cell_connection> connections_on(cell_gid_type gid) const override {
+        if(gid % params_.n_cells_per_cable || (int)gid - 1 < 0) {
+            return{};
+        }
+        return {arb::cell_connection({gid - 1, 0}, {gid, 0}, params_.event_weight, params_.event_min_delay)};
     }
 
     // There is one probe (for measuring voltage at the soma) on the cell.
@@ -94,14 +104,22 @@ public:
     std::vector<arb::gap_junction_connection> gap_junctions_on(cell_gid_type gid) const override{
         std::vector<arb::gap_junction_connection> conns;
 
-        cell_gid_type ring_start_gid = (gid/params_.cells_per_ring) * params_.cells_per_ring;
-        cell_gid_type next_cell = (gid + 1) % params_.cells_per_ring + ring_start_gid;
-        cell_gid_type prev_cell = (gid - 1 + params_.cells_per_ring) % params_.cells_per_ring + ring_start_gid;
+        int cable_begin = (gid/params_.n_cells_per_cable) * params_.n_cells_per_cable;
+        int cable_end = cable_begin + params_.n_cells_per_cable;
+
+        int next_cell = gid + 1;
+        int prev_cell = gid - 1;
+
+        // Soma is connected to the prev cell's dendrite
+        // Dendrite is connected to the next cell's soma
+        // Gap junction conductance in μS
 
-        // Our soma is connected to the next cell's dendrite
-        // Our dendrite is connected to the prev cell's soma
-        conns.push_back(arb::gap_junction_connection({next_cell, 1}, {gid, 0}, 0.0037)); // 1 is the id of the dendrite junction
-        conns.push_back(arb::gap_junction_connection({prev_cell, 0}, {gid, 1}, 0.0037)); // 0 is the id of the soma junction
+        if (next_cell < cable_end) {
+            conns.push_back(arb::gap_junction_connection({(cell_gid_type)next_cell, 0}, {gid, 1}, 0.015));
+        }
+        if (prev_cell >= cable_begin) {
+            conns.push_back(arb::gap_junction_connection({(cell_gid_type)prev_cell, 1}, {gid, 0}, 0.015));
+        }
 
         return conns;
     }
@@ -157,16 +175,26 @@ int main(int argc, char** argv) {
     try {
         bool root = true;
 
+        arb::proc_allocation resources;
+        if (auto nt = arbenv::get_env_num_threads()) {
+            resources.num_threads = nt;
+        }
+        else {
+            resources.num_threads = arbenv::thread_concurrency();
+        }
+
 #ifdef ARB_MPI_ENABLED
         arbenv::with_mpi guard(argc, argv, false);
-        auto context = arb::make_context(arb::proc_allocation(), MPI_COMM_WORLD);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
+        auto context = arb::make_context(resources, MPI_COMM_WORLD);
         {
             int rank;
             MPI_Comm_rank(MPI_COMM_WORLD, &rank);
             root = rank==0;
         }
 #else
-        auto context = arb::make_context();
+        resources.gpu_id = arbenv::default_gpu();
+        auto context = arb::make_context(resources);
 #endif
 
 #ifdef ARB_PROFILE_ENABLED
@@ -225,7 +253,7 @@ int main(int argc, char** argv) {
 
         std::cout << "running simulation" << std::endl;
         // Run the simulation for 100 ms, with time steps of 0.025 ms.
-        sim.run(params.duration, 0.025);
+        sim.run(params.sim_duration, 0.025);
 
         meters.checkpoint("model-run", context);
 
@@ -234,7 +262,7 @@ int main(int argc, char** argv) {
         // Write spikes to file
         if (root) {
             std::cout << "\n" << ns << " spikes generated at rate of "
-                      << params.duration/ns << " ms between spikes\n";
+                      << params.sim_duration/ns << " ms between spikes\n";
             std::ofstream fid("spikes.gdf");
             if (!fid.good()) {
                 std::cerr << "Warning: unable to open file spikes.gdf for spike output\n";
@@ -275,6 +303,7 @@ void write_trace_json(const std::vector<arb::trace_data<double>>& trace, unsigne
         json["name"] = "gj demo: cell " + std::to_string(i);
         json["units"] = "mV";
         json["cell"] = std::to_string(i);
+        json["group"] = std::to_string(rank);
         json["probe"] = "0";
 
         auto &jt = json["data"]["time"];
@@ -290,7 +319,7 @@ void write_trace_json(const std::vector<arb::trace_data<double>>& trace, unsigne
     }
 }
 
-arb::mc_cell gj_cell(double delay, double duration) {
+arb::mc_cell gj_cell(cell_gid_type gid, unsigned ncell, double stim_duration) {
     arb::mc_cell cell;
 
     arb::mechanism_desc nax("nax");
@@ -327,11 +356,16 @@ arb::mc_cell gj_cell(double delay, double duration) {
 
     cell.add_detector({0,0}, 10);
 
-    cell.add_gap_junction({0, 1}); //ggap in μS
-    cell.add_gap_junction({1, 1}); //ggap in μS
+    cell.add_gap_junction({0, 1});
+    cell.add_gap_junction({1, 1});
+
+    if (!gid) {
+        arb::i_clamp stim(0, stim_duration, 0.4);
+        cell.add_stimulus({0, 0.5}, stim);
+    }
 
-    arb::i_clamp stim(delay, duration, 0.2);
-    cell.add_stimulus({1, 0.95}, stim);
+    // Add a synapse to the mid point of the first dendrite.
+    cell.add_synapse({1, 0.5}, "expsyn");
 
     return cell;
 }
diff --git a/example/gap_junctions/parameters.hpp b/example/gap_junctions/parameters.hpp
index 2c2e7487..f7dc5f05 100644
--- a/example/gap_junctions/parameters.hpp
+++ b/example/gap_junctions/parameters.hpp
@@ -13,11 +13,13 @@ struct gap_params {
     gap_params() = default;
 
     std::string name = "default";
-    unsigned cells_per_ring = 100;
-    unsigned num_rings = 10;
-    double duration = 100;
-    double delay = 0.5;
-    bool print_all = false;
+    unsigned n_cables = 3;
+    unsigned n_cells_per_cable = 5;
+    double stim_duration = 30;
+    double event_min_delay = 10;
+    double event_weight = 0.05;
+    double sim_duration = 100;
+    bool print_all = true;
 };
 
 gap_params read_options(int argc, char** argv) {
@@ -44,10 +46,12 @@ gap_params read_options(int argc, char** argv) {
     json << f;
 
     param_from_json(params.name, "name", json);
-    param_from_json(params.cells_per_ring, "num-cells", json);
-    param_from_json(params.num_rings, "num-rings", json);
-    param_from_json(params.duration, "duration", json);
-    param_from_json(params.delay, "delay", json);
+    param_from_json(params.n_cables, "n-cables", json);
+    param_from_json(params.n_cells_per_cable, "n-cells-per-cable", json);
+    param_from_json(params.stim_duration, "stim-duration", json);
+    param_from_json(params.event_min_delay, "event-min-delay", json);
+    param_from_json(params.event_weight, "event-weight", json);
+    param_from_json(params.sim_duration, "sim-duration", json);
     param_from_json(params.print_all, "print-all", json);
 
     if (!json.empty()) {
diff --git a/example/gap_junctions/readme.md b/example/gap_junctions/readme.md
index 1d32a192..a338020c 100644
--- a/example/gap_junctions/readme.md
+++ b/example/gap_junctions/readme.md
@@ -1,3 +1,45 @@
 # Gap Junctions Example
 
 A miniapp that demonstrates how to describe how to build a network with gap junctions.
+
+##Structure:
+Cells are structured into groups that are inter-connected by gap junctions; Groups are
+connected by synapses. The first cell of the first group (top left in diagram) has a 
+current stimulus.
+
+
+```
+c --gj-- c --gj-- c --gj-- c --gj-- c
+                                    |
+                                    syn
+                                    |
+c --gj-- c --gj-- c --gj-- c --gj-- c
+|
+syn
+|
+c --gj-- c --gj-- c --gj-- c --gj-- c
+```
+     
+
+## Tunable parameters
+* _n_cables_: number of groups of cells connected by gap junctions.
+* _n_cells_per_cable_: number of cells in a group.
+* _stim_duration_: duration that the stimulus on the first cell is on. 
+* _event_min_delay_: minimum delay of the network.
+* _event_weight_: weight of an event.
+* _sim_duration_: duration of the simulation. 
+* _print_all_: print the voltages of all cells in nerwork.
+
+An example parameter file is:
+```
+{
+    "name": "small test",
+    "n-cables": 3,
+    "n-cells-per-cable": 5,
+    "stim-duration": 30,
+    "event-min-delay": 10,
+    "event-weight": 0.05,
+    "sim-duration": 100, 
+    "print-all": false
+}
+```
\ No newline at end of file
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 1148c002..977372b9 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -103,7 +103,6 @@ set(unit_sources
     test_spike_store.cpp
     test_stats.cpp
     test_strprintf.cpp
-    test_supercell.cpp
     test_swcio.cpp
     test_synapses.cpp
     test_thread.cpp
diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp
index 84e2e226..8e2c2f25 100644
--- a/test/unit/test_fvm_lowered.cpp
+++ b/test/unit/test_fvm_lowered.cpp
@@ -78,6 +78,128 @@ ACCESS_BIND(\
 
 using namespace arb;
 
+class gap_recipe_0: public recipe {
+public:
+    gap_recipe_0() {}
+
+    cell_size_type num_cells() const override {
+        return size_;
+    }
+
+    arb::util::unique_any get_cell_description(cell_gid_type gid) const override {
+        mc_cell c;
+        c.add_soma(20);
+        c.add_gap_junction({0, 1});
+        return {std::move(c)};
+    }
+
+    cell_kind get_cell_kind(cell_gid_type gid) const override {
+        return cell_kind::cable1d_neuron;
+    }
+    std::vector<gap_junction_connection> gap_junctions_on(cell_gid_type gid) const override {
+        switch (gid) {
+            case 0 :
+                return {gap_junction_connection({5, 0}, {0, 0}, 0.1)};
+            case 2 :
+                return {
+                        gap_junction_connection({3, 0}, {2, 0}, 0.1),
+                };
+            case 3 :
+                return {
+                        gap_junction_connection({7, 0}, {3, 0}, 0.1),
+                        gap_junction_connection({3, 0}, {2, 0}, 0.1)
+                };
+            case 5 :
+                return {gap_junction_connection({5, 0}, {0, 0}, 0.1)};
+            case 7 :
+                return {
+                        gap_junction_connection({3, 0}, {7, 0}, 0.1),
+                };
+            default :
+                return {};
+        }
+    }
+
+private:
+    cell_size_type size_ = 12;
+};
+
+class gap_recipe_1: public recipe {
+public:
+    gap_recipe_1() {}
+
+    cell_size_type num_cells() const override {
+        return size_;
+    }
+
+    arb::util::unique_any get_cell_description(cell_gid_type) const override {
+        mc_cell c;
+        c.add_soma(20);
+        return {std::move(c)};
+    }
+
+    cell_kind get_cell_kind(cell_gid_type gid) const override {
+        return cell_kind::cable1d_neuron;
+    }
+
+private:
+    cell_size_type size_ = 12;
+};
+
+class gap_recipe_2: public recipe {
+public:
+    gap_recipe_2() {}
+
+    cell_size_type num_cells() const override {
+        return size_;
+    }
+
+    arb::util::unique_any get_cell_description(cell_gid_type) const override {
+        mc_cell c;
+        c.add_soma(20);
+        c.add_gap_junction({0,1});
+        return {std::move(c)};
+    }
+
+    cell_kind get_cell_kind(cell_gid_type gid) const override {
+        return cell_kind::cable1d_neuron;
+    }
+    std::vector<gap_junction_connection> gap_junctions_on(cell_gid_type gid) const override {
+        switch (gid) {
+            case 0 :
+                return {
+                        gap_junction_connection({2, 0}, {0, 0}, 0.1),
+                        gap_junction_connection({3, 0}, {0, 0}, 0.1),
+                        gap_junction_connection({5, 0}, {0, 0}, 0.1)
+                };
+            case 2 :
+                return {
+                        gap_junction_connection({0, 0}, {2, 0}, 0.1),
+                        gap_junction_connection({3, 0}, {2, 0}, 0.1),
+                        gap_junction_connection({5, 0}, {2, 0}, 0.1)
+                };
+            case 3 :
+                return {
+                        gap_junction_connection({0, 0}, {3, 0}, 0.1),
+                        gap_junction_connection({2, 0}, {3, 0}, 0.1),
+                        gap_junction_connection({5, 0}, {3, 0}, 0.1)
+                };
+            case 5 :
+                return {
+                        gap_junction_connection({2, 0}, {5, 0}, 0.1),
+                        gap_junction_connection({3, 0}, {5, 0}, 0.1),
+                        gap_junction_connection({0, 0}, {5, 0}, 0.1)
+                };
+            default :
+                return {};
+        }
+    }
+
+private:
+    cell_size_type size_ = 12;
+};
+
+
 TEST(fvm_lowered, matrix_init)
 {
     execution_context context;
@@ -92,10 +214,11 @@ TEST(fvm_lowered, matrix_init)
     cell.segment(1)->set_compartments(10);
 
     std::vector<target_handle> targets;
+    std::vector<fvm_index_type> cell_to_intdom;
     probe_association_map<probe_handle> probe_map;
 
     fvm_cell fvcell(context);
-    fvcell.initialize({0}, {0}, cable1d_recipe(cell), targets, probe_map);
+    fvcell.initialize({0}, cable1d_recipe(cell), cell_to_intdom, targets, probe_map);
 
     auto& J = fvcell.*private_matrix_ptr;
     EXPECT_EQ(J.size(), 11u);
@@ -137,10 +260,11 @@ TEST(fvm_lowered, target_handles) {
     cells[1].add_detector({0, 0}, 3.3);
 
     std::vector<target_handle> targets;
+    std::vector<fvm_index_type> cell_to_intdom;
     probe_association_map<probe_handle> probe_map;
 
     fvm_cell fvcell(context);
-    fvcell.initialize({0, 1}, {0, 0}, cable1d_recipe(cells), targets, probe_map);
+    fvcell.initialize({0, 1}, cable1d_recipe(cells), cell_to_intdom, targets, probe_map);
 
     mechanism* expsyn = find_mechanism(fvcell, "expsyn");
     ASSERT_TRUE(expsyn);
@@ -154,19 +278,19 @@ TEST(fvm_lowered, target_handles) {
 
     EXPECT_EQ(expsyn_id, targets[0].mech_id);
     EXPECT_EQ(1u, targets[0].mech_index);
-    EXPECT_EQ(0u, targets[0].cell_index);
+    EXPECT_EQ(0u, targets[0].intdom_index);
 
     EXPECT_EQ(expsyn_id, targets[1].mech_id);
     EXPECT_EQ(0u, targets[1].mech_index);
-    EXPECT_EQ(0u, targets[1].cell_index);
+    EXPECT_EQ(0u, targets[1].intdom_index);
 
     EXPECT_EQ(exp2syn_id, targets[2].mech_id);
     EXPECT_EQ(0u, targets[2].mech_index);
-    EXPECT_EQ(1u, targets[2].cell_index);
+    EXPECT_EQ(1u, targets[2].intdom_index);
 
     EXPECT_EQ(expsyn_id, targets[3].mech_id);
     EXPECT_EQ(2u, targets[3].mech_index);
-    EXPECT_EQ(1u, targets[3].cell_index);
+    EXPECT_EQ(1u, targets[3].intdom_index);
 }
 
 TEST(fvm_lowered, stimulus) {
@@ -196,6 +320,7 @@ TEST(fvm_lowered, stimulus) {
     // The implementation of the stimulus is tested by creating a lowered cell, then
     // testing that the correct currents are injected at the correct control volumes
     // as during the stimulus windows.
+    std::vector<fvm_index_type> cell_to_intdom(cells.size(), 0);
 
     fvm_discretization D = fvm_discretize(cells);
     const auto& A = D.cv_area;
@@ -204,7 +329,7 @@ TEST(fvm_lowered, stimulus) {
     probe_association_map<probe_handle> probe_map;
 
     fvm_cell fvcell(context);
-    fvcell.initialize({0}, {0}, cable1d_recipe(cells), targets, probe_map);
+    fvcell.initialize({0}, cable1d_recipe(cells), cell_to_intdom, targets, probe_map);
 
     mechanism* stim = find_mechanism(fvcell, "_builtin_stimulus");
     ASSERT_TRUE(stim);
@@ -294,11 +419,12 @@ TEST(fvm_lowered, derived_mechs) {
         // Test initialization and global parameter values.
 
         std::vector<target_handle> targets;
+        std::vector<fvm_index_type> cell_to_intdom;
         probe_association_map<probe_handle> probe_map;
 
         execution_context context;
         fvm_cell fvcell(context);
-        fvcell.initialize({0, 1, 2}, {0, 0, 0}, rec, targets, probe_map);
+        fvcell.initialize({0, 1, 2}, rec, cell_to_intdom, targets, probe_map);
 
         // Both mechanisms will have the same internal name, "test_kin1".
 
@@ -401,10 +527,11 @@ TEST(fvm_lowered, weighted_write_ion) {
     c.segments()[3] ->add_mechanism("test_ca");
 
     std::vector<target_handle> targets;
+    std::vector<fvm_index_type> cell_to_intdom;
     probe_association_map<probe_handle> probe_map;
 
     fvm_cell fvcell(context);
-    fvcell.initialize({0}, {0}, cable1d_recipe(c), targets, probe_map);
+    fvcell.initialize({0}, cable1d_recipe(c), cell_to_intdom, targets, probe_map);
 
     auto& state = *(fvcell.*private_state_ptr).get();
     auto& ion = state.ion_data.at(ionKind::ca);
@@ -591,9 +718,13 @@ TEST(fvm_lowered, gj_coords_complex) {
     cells.push_back(std::move(c1));
     cells.push_back(std::move(c2));
 
-    fvm_discretization D = fvm_discretize(cells);
+    std::vector<fvm_index_type> cell_to_intdom;
+
     std::vector<cell_gid_type> gids = {0, 1, 2};
 
+    fvcell.fvm_intdom(rec, gids, cell_to_intdom);
+    fvm_discretization D = fvm_discretize(cells);
+
     auto GJ = fvcell.fvm_gap_junctions(cells, gids, rec, D);
     EXPECT_EQ(10u, GJ.size());
 
@@ -661,18 +792,22 @@ TEST(fvm_lowered, cell_group_gj) {
         c.add_soma(2.1);
         if (i % 2 == 0) {
             c.add_gap_junction({0, 1});
-            if (i < 10) {
-                cell_group0.push_back(std::move(c));
-            }
-            else {
-                cell_group1.push_back(std::move(c));
-            }
         }
-
+        if (i < 10) {
+            cell_group0.push_back(std::move(c));
+        }
+        else {
+            cell_group1.push_back(std::move(c));
+        }
     }
 
-    std::vector<cell_gid_type> gids_cg0 = { 0, 2, 4, 6, 8};
-    std::vector<cell_gid_type> gids_cg1 = {10,12,14,16,18};
+    std::vector<cell_gid_type> gids_cg0 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    std::vector<cell_gid_type> gids_cg1 = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+    std::vector<fvm_index_type> cell_to_intdom0, cell_to_intdom1;
+
+    auto num_dom0 = fvcell.fvm_intdom(rec, gids_cg0, cell_to_intdom0);
+    auto num_dom1 = fvcell.fvm_intdom(rec, gids_cg1, cell_to_intdom1);
 
     fvm_discretization D0 = fvm_discretize(cell_group0);
     fvm_discretization D1 = fvm_discretize(cell_group1);
@@ -683,10 +818,61 @@ TEST(fvm_lowered, cell_group_gj) {
     EXPECT_EQ(10u, GJ0.size());
     EXPECT_EQ(10u, GJ1.size());
 
-    std::vector<pair> expected_loc = {{0, 1}, {0, 4}, {1, 2}, {1, 0}, {2, 3} ,{2, 1}, {3, 4}, {3, 2}, {4, 0}, {4, 3}};
+    std::vector<pair> expected_loc = {{0, 2}, {0, 8}, {2, 4}, {2, 0}, {4, 6} ,{4, 2}, {6, 8}, {6, 4}, {8, 0}, {8, 6}};
 
     for (unsigned i = 0; i < GJ0.size(); i++) {
         EXPECT_EQ(expected_loc[i], GJ0[i].loc);
         EXPECT_EQ(expected_loc[i], GJ1[i].loc);
     }
+
+    std::vector<fvm_index_type> expected_doms= {0u, 1u, 0u, 2u, 0u, 3u, 0u, 4u, 0u, 5u};
+    EXPECT_EQ(6u, num_dom0);
+    EXPECT_EQ(6u, num_dom1);
+
+    EXPECT_EQ(expected_doms, cell_to_intdom0);
+    EXPECT_EQ(expected_doms, cell_to_intdom1);
+
 }
+
+TEST(fvm_lowered, integration_domains) {
+    {
+        execution_context context;
+        fvm_cell fvcell(context);
+
+        std::vector<cell_gid_type> gids = {11u, 5u, 2u, 3u, 0u, 8u, 7u};
+        std::vector<fvm_index_type> cell_to_intdom;
+
+        auto num_dom = fvcell.fvm_intdom(gap_recipe_0(), gids, cell_to_intdom);
+        std::vector<fvm_index_type> expected_doms= {0u, 1u, 2u, 2u, 1u, 3u, 2u};
+
+        EXPECT_EQ(4u, num_dom);
+        EXPECT_EQ(expected_doms, cell_to_intdom);
+    }
+    {
+        execution_context context;
+        fvm_cell fvcell(context);
+
+        std::vector<cell_gid_type> gids = {11u, 5u, 2u, 3u, 0u, 8u, 7u};
+        std::vector<fvm_index_type> cell_to_intdom;
+
+        auto num_dom = fvcell.fvm_intdom(gap_recipe_1(), gids, cell_to_intdom);
+        std::vector<fvm_index_type> expected_doms= {0u, 1u, 2u, 3u, 4u, 5u, 6u};
+
+        EXPECT_EQ(7u, num_dom);
+        EXPECT_EQ(expected_doms, cell_to_intdom);
+    }
+    {
+        execution_context context;
+        fvm_cell fvcell(context);
+
+        std::vector<cell_gid_type> gids = {5u, 2u, 3u, 0u};
+        std::vector<fvm_index_type> cell_to_intdom;
+
+        auto num_dom = fvcell.fvm_intdom(gap_recipe_2(), gids, cell_to_intdom);
+        std::vector<fvm_index_type> expected_doms= {0u, 0u, 0u, 0u};
+
+        EXPECT_EQ(1u, num_dom);
+        EXPECT_EQ(expected_doms, cell_to_intdom);
+    }
+}
+
diff --git a/test/unit/test_matrix.cpp b/test/unit/test_matrix.cpp
index c977f88b..eb72dacf 100644
--- a/test/unit/test_matrix.cpp
+++ b/test/unit/test_matrix.cpp
@@ -23,7 +23,7 @@ using vvec = std::vector<value_type>;
 TEST(matrix, construct_from_parent_only)
 {
     std::vector<index_type> p = {0,0,1};
-    matrix_type m(p, {0, 3}, vvec(3), vvec(3), vvec(3));
+    matrix_type m(p, {0, 3}, vvec(3), vvec(3), vvec(3), {0});
     EXPECT_EQ(m.num_cells(), 1u);
     EXPECT_EQ(m.size(), 3u);
     EXPECT_EQ(p.size(), 3u);
@@ -41,7 +41,7 @@ TEST(matrix, solve_host)
 
     // trivial case : 1x1 matrix
     {
-        matrix_type m({0}, {0,1}, vvec(1), vvec(1), vvec(1));
+        matrix_type m({0}, {0,1}, vvec(1), vvec(1), vvec(1), {0});
         auto& state = m.state_;
         fill(state.d,  2);
         fill(state.u, -1);
@@ -57,7 +57,7 @@ TEST(matrix, solve_host)
         for(auto n : make_span(2, 1001)) {
             auto p = std::vector<index_type>(n);
             std::iota(p.begin()+1, p.end(), 0);
-            matrix_type m(p, {0, n}, vvec(n), vvec(n), vvec(n));
+            matrix_type m(p, {0, n}, vvec(n), vvec(n), vvec(n), {0});
 
             EXPECT_EQ(m.size(), (unsigned)n);
             EXPECT_EQ(m.num_cells(), 1u);
@@ -94,7 +94,8 @@ TEST(matrix, zero_diagonal)
     // Three matrices, sizes 3, 3 and 2, with no branching.
     std::vector<index_type> p = {0, 0, 1, 3, 3, 5, 5};
     std::vector<index_type> c = {0, 3, 5, 7};
-    matrix_type m(p, c, vvec(7), vvec(7), vvec(7));
+    std::vector<index_type> i = {0, 1, 2};
+    matrix_type m(p, c, vvec(7), vvec(7), vvec(7), i);
 
     EXPECT_EQ(7u, m.size());
     EXPECT_EQ(3u, m.num_cells());
@@ -129,6 +130,7 @@ TEST(matrix, zero_diagonal_assembled)
     // Three matrices, sizes 3, 3 and 2, with no branching.
     std::vector<index_type> p = {0, 0, 1, 3, 3, 5, 5};
     std::vector<index_type> c = {0, 3, 5, 7};
+    std::vector<index_type> s = {0, 1, 2};
 
     // Face conductances.
     vvec g = {0, 1, 1, 0, 1, 0, 2};
@@ -152,7 +154,7 @@ TEST(matrix, zero_diagonal_assembled)
     // Expected solution:
     // x = [ 4  5  6  7  8  9 10]
 
-    matrix_type m(p, c, Cm, g, area);
+    matrix_type m(p, c, Cm, g, area, s);
     m.assemble(dt, v, i);
     m.solve();
 
diff --git a/test/unit/test_matrix.cu b/test/unit/test_matrix.cu
index b3424ad2..8d01632d 100644
--- a/test/unit/test_matrix.cu
+++ b/test/unit/test_matrix.cu
@@ -256,6 +256,7 @@ TEST(matrix, backends)
 
     std::vector<I> p;
     std::vector<I> cell_cv_divs;
+    std::vector<I> cell_to_intdom;
     for (auto m=0; m<num_mtx; ++m) {
         auto &p_ref = p_base[m%2];
         auto first = p.size();
@@ -263,6 +264,7 @@ TEST(matrix, backends)
             p.push_back(i + first);
         }
         cell_cv_divs.push_back(first);
+        cell_to_intdom.push_back(m);
     }
     cell_cv_divs.push_back(p.size());
 
@@ -286,9 +288,9 @@ TEST(matrix, backends)
     std::generate(i.begin(), i.end(), [&](){return dist(gen);});
 
     // Make the reference matrix and the gpu matrix
-    auto flat = state_flat(p, cell_cv_divs, Cm, g, area); // flat
-    auto intl = state_intl(p, cell_cv_divs, Cm, g, area); // interleaved
-    auto fine = state_fine(p, cell_cv_divs, Cm, g, area); // interleaved
+    auto flat = state_flat(p, cell_cv_divs, Cm, g, area, cell_to_intdom); // flat
+    auto intl = state_intl(p, cell_cv_divs, Cm, g, area, cell_to_intdom); // interleaved
+    auto fine = state_fine(p, cell_cv_divs, Cm, g, area, cell_to_intdom); // interleaved
 
     // Set the integration times for the cells to be between 0.01 and 0.02 ms.
     std::vector<T> dt(num_mtx, 0);
diff --git a/test/unit/test_matrix_cpuvsgpu.cpp b/test/unit/test_matrix_cpuvsgpu.cpp
index 861c83dd..9d90e062 100644
--- a/test/unit/test_matrix_cpuvsgpu.cpp
+++ b/test/unit/test_matrix_cpuvsgpu.cpp
@@ -86,6 +86,7 @@ TEST(matrix, assemble)
 
     std::vector<I> p;
     std::vector<I> cell_index;
+    std::vector<I> intdom_index;
     for (auto m=0; m<num_mtx; ++m) {
         auto &p_ref = p_base[m%p_base.size()];
         auto first = p.size();
@@ -93,6 +94,7 @@ TEST(matrix, assemble)
             p.push_back(i + first);
         }
         cell_index.push_back(first);
+        intdom_index.push_back(m);
     }
     cell_index.push_back(p.size());
 
@@ -112,8 +114,8 @@ TEST(matrix, assemble)
     std::vector<T> area(group_size, 1e3);
 
     // Make the reference matrix and the gpu matrix
-    auto m_mc  = mc_state( p, cell_index, Cm, g, area); // on host
-    auto m_gpu = gpu_state(p, cell_index, Cm, g, area); // on gpu
+    auto m_mc  = mc_state( p, cell_index, Cm, g, area, intdom_index); // on host
+    auto m_gpu = gpu_state(p, cell_index, Cm, g, area, intdom_index); // on gpu
 
     // Set the integration times for the cells to be between 0.1 and 0.2 ms.
     std::vector<T> dt(num_mtx);
diff --git a/test/unit/test_mc_cell_group.cpp b/test/unit/test_mc_cell_group.cpp
index 70077f32..11e33300 100644
--- a/test/unit/test_mc_cell_group.cpp
+++ b/test/unit/test_mc_cell_group.cpp
@@ -28,128 +28,6 @@ namespace {
 
         return c;
     }
-
-    class gap_recipe_0: public recipe {
-    public:
-        gap_recipe_0() {}
-
-        cell_size_type num_cells() const override {
-            return size_;
-        }
-
-        arb::util::unique_any get_cell_description(cell_gid_type gid) const override {
-            mc_cell c;
-            c.add_soma(20);
-            c.add_gap_junction({0, 1});
-            return {std::move(c)};
-        }
-
-        cell_kind get_cell_kind(cell_gid_type gid) const override {
-            return cell_kind::cable1d_neuron;
-        }
-        std::vector<gap_junction_connection> gap_junctions_on(cell_gid_type gid) const override {
-            switch (gid) {
-                case 0 :
-                    return {gap_junction_connection({5, 0}, {0, 0}, 0.1)};
-                case 2 :
-                    return {
-                            gap_junction_connection({3, 0}, {2, 0}, 0.1),
-                    };
-                case 3 :
-                    return {
-                            gap_junction_connection({7, 0}, {3, 0}, 0.1),
-                            gap_junction_connection({3, 0}, {2, 0}, 0.1)
-                    };
-                case 5 :
-                    return {gap_junction_connection({5, 0}, {0, 0}, 0.1)};
-                case 7 :
-                    return {
-                            gap_junction_connection({3, 0}, {7, 0}, 0.1),
-                    };
-                default :
-                    return {};
-            }
-        }
-
-    private:
-        cell_size_type size_ = 12;
-    };
-
-    class gap_recipe_1: public recipe {
-    public:
-        gap_recipe_1() {}
-
-        cell_size_type num_cells() const override {
-            return size_;
-        }
-
-        arb::util::unique_any get_cell_description(cell_gid_type) const override {
-            mc_cell c;
-            c.add_soma(20);
-            return {std::move(c)};
-        }
-
-        cell_kind get_cell_kind(cell_gid_type gid) const override {
-            return cell_kind::cable1d_neuron;
-        }
-
-    private:
-        cell_size_type size_ = 12;
-    };
-
-    class gap_recipe_2: public recipe {
-    public:
-        gap_recipe_2() {}
-
-        cell_size_type num_cells() const override {
-            return size_;
-        }
-
-        arb::util::unique_any get_cell_description(cell_gid_type) const override {
-            mc_cell c;
-            c.add_soma(20);
-            c.add_gap_junction({0,1});
-            return {std::move(c)};
-        }
-
-        cell_kind get_cell_kind(cell_gid_type gid) const override {
-            return cell_kind::cable1d_neuron;
-        }
-        std::vector<gap_junction_connection> gap_junctions_on(cell_gid_type gid) const override {
-            switch (gid) {
-                case 0 :
-                    return {
-                            gap_junction_connection({2, 0}, {0, 0}, 0.1),
-                            gap_junction_connection({3, 0}, {0, 0}, 0.1),
-                            gap_junction_connection({5, 0}, {0, 0}, 0.1)
-                    };
-                case 2 :
-                    return {
-                            gap_junction_connection({0, 0}, {2, 0}, 0.1),
-                            gap_junction_connection({3, 0}, {2, 0}, 0.1),
-                            gap_junction_connection({5, 0}, {2, 0}, 0.1)
-                    };
-                case 3 :
-                    return {
-                            gap_junction_connection({0, 0}, {3, 0}, 0.1),
-                            gap_junction_connection({2, 0}, {3, 0}, 0.1),
-                            gap_junction_connection({5, 0}, {3, 0}, 0.1)
-                    };
-                case 5 :
-                    return {
-                            gap_junction_connection({2, 0}, {5, 0}, 0.1),
-                            gap_junction_connection({3, 0}, {5, 0}, 0.1),
-                            gap_junction_connection({0, 0}, {5, 0}, 0.1)
-                    };
-                default :
-                    return {};
-            }
-        }
-
-    private:
-        cell_size_type size_ = 12;
-    };
-
 }
 
 ACCESS_BIND(
@@ -207,30 +85,3 @@ TEST(mc_cell_group, sources) {
     }
 }
 
-TEST(mc_cell_group, generated_gids_deps_) {
-    {
-        std::vector<cell_gid_type> gids = {11u, 5u, 2u, 3u, 0u, 8u, 7u};
-        mc_cell_group group{gids, gap_recipe_0(), lowered_cell()};
-
-        std::vector<cell_gid_type> expected_gids = {11u, 5u, 0u, 2u, 3u, 7u, 8u};
-        std::vector<int> expected_deps = {0, 2, 0, 3, 0, 0, 0};
-        EXPECT_EQ(expected_gids, group.get_gids());
-        EXPECT_EQ(expected_deps, group.get_dependencies());
-    }
-    {
-        std::vector<cell_gid_type> gids = {11u, 5u, 2u, 3u, 0u, 8u, 7u};
-        mc_cell_group group{gids, gap_recipe_1(), lowered_cell()};
-
-        std::vector<int> expected_deps = {0, 0, 0, 0, 0, 0, 0};
-        EXPECT_EQ(gids, group.get_gids());
-        EXPECT_EQ(expected_deps, group.get_dependencies());
-    }
-    {
-        std::vector<cell_gid_type> gids = {5u, 2u, 3u, 0u};
-        mc_cell_group group{gids, gap_recipe_2(), lowered_cell()};
-
-        std::vector<int> expected_deps = {4, 0, 0, 0};
-        EXPECT_EQ(gids, group.get_gids());
-        EXPECT_EQ(expected_deps, group.get_dependencies());
-    }
-}
diff --git a/test/unit/test_mech_temperature.cpp b/test/unit/test_mech_temperature.cpp
index 81a11394..e4e10226 100644
--- a/test/unit/test_mech_temperature.cpp
+++ b/test/unit/test_mech_temperature.cpp
@@ -22,13 +22,12 @@ void run_celsius_test() {
 
     fvm_size_type ncell = 1;
     fvm_size_type ncv = 3;
-    std::vector<fvm_index_type> cv_to_cell(ncv, 0);
+    std::vector<fvm_index_type> cv_to_intdom(ncv, 0);
 
     std::vector<fvm_gap_junction> gj = {};
-    std::vector<int> deps = {0};
     auto celsius_test = cat.instance<backend>("celsius_test");
     auto shared_state = std::make_unique<typename backend::shared_state>(
-        ncell, cv_to_cell, deps, gj, celsius_test->data_alignment());
+        ncell, cv_to_intdom, gj, celsius_test->data_alignment());
 
     mechanism::layout layout;
     layout.weight.assign(ncv, 1.);
diff --git a/test/unit/test_probe.cpp b/test/unit/test_probe.cpp
index 2e52784a..6f449cb1 100644
--- a/test/unit/test_probe.cpp
+++ b/test/unit/test_probe.cpp
@@ -37,10 +37,11 @@ TEST(probe, fvm_lowered_cell) {
     rec.add_probe(0, 30, cell_probe_address{loc2, cell_probe_address::membrane_current});
 
     std::vector<target_handle> targets;
+    std::vector<fvm_index_type> cell_to_intdom;
     probe_association_map<probe_handle> probe_map;
 
     fvm_cell lcell(context);
-    lcell.initialize({0}, {0}, rec, targets, probe_map);
+    lcell.initialize({0}, rec, cell_to_intdom, targets, probe_map);
 
     EXPECT_EQ(3u, rec.num_probes(0));
     EXPECT_EQ(3u, probe_map.size());
diff --git a/test/unit/test_supercell.cpp b/test/unit/test_supercell.cpp
deleted file mode 100644
index f4bebb2c..00000000
--- a/test/unit/test_supercell.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "../gtest.h"
-
-#include <cmath>
-#include <tuple>
-#include <vector>
-
-#include <arbor/constants.hpp>
-#include <arbor/mechcat.hpp>
-#include <arbor/util/optional.hpp>
-#include <arbor/mc_cell.hpp>
-
-#include "backends/multicore/fvm.hpp"
-#include "backends/multicore/mechanism.hpp"
-#include "util/maputil.hpp"
-#include "util/range.hpp"
-
-#include "common.hpp"
-#include "mech_private_field_access.hpp"
-
-using namespace arb;
-
-using backend = ::arb::multicore::backend;
-using shared_state = backend::shared_state;
-using value_type = backend::value_type;
-using size_type = backend::size_type;
-
-// Access to more mechanism protected data:
-
-ACCESS_BIND(const value_type* multicore::mechanism::*, vec_v_ptr, &multicore::mechanism::vec_v_)
-ACCESS_BIND(value_type* multicore::mechanism::*, vec_i_ptr, &multicore::mechanism::vec_i_)
-
-TEST(supercell, sync_time_to) {
-    using value_type = multicore::backend::value_type;
-    using index_type = multicore::backend::index_type;
-
-    int num_cell = 10;
-
-    std::vector<fvm_gap_junction> gj = {};
-    std::vector<index_type> deps = {4, 0, 0, 0, 3, 0, 0, 2, 0, 0};
-
-    shared_state state(num_cell, std::vector<index_type>(num_cell, 0), deps, gj, 1u);
-
-    state.time_to = {0.3, 0.1, 0.2, 0.4, 0.5, 0.6, 0.1, 0.1, 0.6, 0.9};
-    state.sync_time_to();
-
-    std::vector<value_type> expected = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.9};
-
-    for (unsigned i = 0; i < state.time_to.size(); i++) {
-        EXPECT_EQ(expected[i], state.time_to[i]);
-    }
-
-    state.time_dep = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    state.time_to = {0.3, 0.1, 0.2, 0.4, 0.5, 0.6, 0.1, 0.1, 0.6, 0.9};
-    expected      = {0.3, 0.1, 0.2, 0.4, 0.5, 0.6, 0.1, 0.1, 0.6, 0.9};
-    state.sync_time_to();
-
-    for (unsigned i = 0; i < state.time_to.size(); i++) {
-        EXPECT_EQ(expected[i], state.time_to[i]);
-    }
-
-    state.time_dep = {10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-    state.time_to = {0.3, 0.1, 0.2, 0.4, 0.5, 0.6, 0.1, 0.1, 0.6, 0.9};
-    expected      = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
-    state.sync_time_to();
-
-    for (unsigned i = 0; i < state.time_to.size(); i++) {
-        EXPECT_EQ(expected[i], state.time_to[i]);
-    }
-
-}
-
diff --git a/test/unit/test_synapses.cpp b/test/unit/test_synapses.cpp
index 5210a7d4..9219edbd 100644
--- a/test/unit/test_synapses.cpp
+++ b/test/unit/test_synapses.cpp
@@ -77,7 +77,7 @@ TEST(synapses, syn_basic_state) {
 
     int num_syn = 4;
     int num_comp = 4;
-    int num_cell = 1;
+    int num_intdom = 1;
 
     auto expsyn = unique_cast<multicore::mechanism>(global_default_catalogue().instance<backend>("expsyn"));
     ASSERT_TRUE(expsyn);
@@ -87,7 +87,7 @@ TEST(synapses, syn_basic_state) {
 
     std::vector<fvm_gap_junction> gj = {};
     auto align = std::max(expsyn->data_alignment(), exp2syn->data_alignment());
-    shared_state state(num_cell, std::vector<index_type>(num_comp, 0), std::vector<index_type>(num_cell, 0), gj, align);
+    shared_state state(num_intdom, std::vector<index_type>(num_comp, 0), gj, align);
 
     state.reset(-65., constant::hh_squid_temp);
     fill(state.current_density, 1.0);
-- 
GitLab