diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp
index 474c57f3af6be715c3979e9d0d89e52c231326ba..fe06203a694ec76978ed4f4b6d94d3397f452bbe 100644
--- a/miniapp/miniapp.cpp
+++ b/miniapp/miniapp.cpp
@@ -34,7 +34,6 @@ using file_export_type = io::exporter_spike_file<global_policy>;
 void banner();
 std::unique_ptr<recipe> make_recipe(const io::cl_options&, const probe_distribution&);
 std::unique_ptr<sample_trace_type> make_trace(cell_member_type probe_id, probe_spec probe);
-std::pair<cell_gid_type, cell_gid_type> distribute_cells(cell_size_type ncells);
 using communicator_type = communication::communicator<communication::global_policy>;
 
 void write_trace_json(const sample_trace_type& trace, const std::string& prefix = "trace_");
@@ -77,16 +76,6 @@ int main(int argc, char** argv) {
         pdist.all_segments = !options.probe_soma_only;
 
         auto recipe = make_recipe(options, pdist);
-        auto cell_range = distribute_cells(recipe->num_cells());
-
-        std::vector<cell_gid_type> group_divisions;
-        for (auto i = cell_range.first; i<cell_range.second; i+=options.group_size) {
-            group_divisions.push_back(i);
-        }
-        group_divisions.push_back(cell_range.second);
-
-        EXPECTS(group_divisions.front() == cell_range.first);
-        EXPECTS(group_divisions.back() == cell_range.second);
 
         auto register_exporter = [] (const io::cl_options& options) {
             return
@@ -95,9 +84,14 @@ int main(int argc, char** argv) {
                     options.file_extension, options.over_write);
         };
 
-        model m(*recipe,
-                util::partition_view(group_divisions),
-                config::has_cuda? backend_policy::prefer_gpu: backend_policy::use_multicore);
+        group_rules rules;
+        rules.policy = config::has_cuda?
+            backend_policy::prefer_gpu: backend_policy::use_multicore;
+        rules.target_group_size = options.group_size;
+        auto decomp = domain_decomposition(*recipe, rules);
+
+        model m(*recipe, decomp);
+
         if (options.report_compartments) {
             report_compartment_stats(*recipe);
         }
@@ -111,8 +105,7 @@ int main(int argc, char** argv) {
         m.set_binning_policy(binning_policy, options.bin_dt);
 
         // Inject some artificial spikes, 1 per 20 neurons.
-        cell_gid_type first_spike_cell = 20*((cell_range.first+19)/20);
-        for (auto c=first_spike_cell; c<cell_range.second; c+=20) {
+        for (cell_gid_type c=0; c<recipe->num_cells(); c+=20) {
             m.add_artificial_spike({c, 0});
         }
 
@@ -179,20 +172,6 @@ int main(int argc, char** argv) {
     return 0;
 }
 
-std::pair<cell_gid_type, cell_gid_type> distribute_cells(cell_size_type num_cells) {
-    // Crude load balancing:
-    // divide [0, num_cells) into num_domains non-overlapping, contiguous blocks
-    // of size as close to equal as possible.
-
-    auto num_domains = communication::global_policy::size();
-    auto domain_id = communication::global_policy::id();
-
-    cell_gid_type cell_from = (cell_gid_type)(num_cells*(domain_id/(double)num_domains));
-    cell_gid_type cell_to = (cell_gid_type)(num_cells*((domain_id+1)/(double)num_domains));
-
-    return {cell_from, cell_to};
-}
-
 void banner() {
     std::cout << "====================\n";
     std::cout << "  starting miniapp\n";
diff --git a/src/domain_decomposition.hpp b/src/domain_decomposition.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a4d5d406f794d4dab85e4f249870de8c33c4f495
--- /dev/null
+++ b/src/domain_decomposition.hpp
@@ -0,0 +1,155 @@
+#pragma once
+
+#include <vector>
+
+#include <backends.hpp>
+#include <common_types.hpp>
+#include <communication/global_policy.hpp>
+#include <recipe.hpp>
+#include <util/optional.hpp>
+#include <util/partition.hpp>
+#include <util/transform.hpp>
+
+namespace nest {
+namespace mc {
+
+// Meta data used to guide the domain_decomposition in distributing
+// and grouping cells.
+struct group_rules {
+    cell_size_type target_group_size;
+    backend_policy policy;
+};
+
+class domain_decomposition {
+    using gid_partition_type =
+        util::partition_range<std::vector<cell_gid_type>::const_iterator>;
+
+public:
+    /// Utility type for meta data for a local cell group.
+    struct group_range_type {
+        cell_gid_type begin;
+        cell_gid_type end;
+        cell_kind kind;
+    };
+
+    domain_decomposition(const recipe& rec, const group_rules& rules):
+        backend_policy_(rules.policy)
+    {
+        EXPECTS(rules.target_group_size>0);
+
+        auto num_domains = communication::global_policy::size();
+        auto domain_id = communication::global_policy::id();
+
+        // Partition the cells globally across the domains.
+        num_global_cells_ = rec.num_cells();
+        cell_begin_ = (cell_gid_type)(num_global_cells_*(domain_id/(double)num_domains));
+        cell_end_ = (cell_gid_type)(num_global_cells_*((domain_id+1)/(double)num_domains));
+
+        // Partition the local cells into cell groups that satisfy three
+        // criteria:
+        //  1. the cells in a group have contiguous gid
+        //  2. the size of a cell group does not exceed rules.target_group_size;
+        //  3. all cells in a cell group have the same cell_kind.
+        // This simple greedy algorithm appends contiguous cells to a cell
+        // group until either the target group size is reached, or a cell with a
+        // different kind is encountered.
+        // On completion, cell_starts_ partitions the local gid into cell
+        // groups, and group_kinds_ records the cell kind in each cell group.
+        if (num_local_cells()>0) {
+            cell_size_type group_size = 1;
+
+            // 1st group starts at cell_begin_
+            group_starts_.push_back(cell_begin_);
+            auto group_kind = rec.get_cell_kind(cell_begin_);
+
+            // set kind for 1st group
+            group_kinds_.push_back(group_kind);
+
+            cell_gid_type gid = cell_begin_+1;
+            while (gid<cell_end_) {
+                auto kind = rec.get_cell_kind(gid);
+
+                // Test if gid belongs to a new cell group, i.e. whether it has
+                // a new cell_kind or if the target group size has been reached.
+                if (kind!=group_kind || group_size>=rules.target_group_size) {
+                    group_starts_.push_back(gid);
+                    group_kinds_.push_back(kind);
+                    group_size = 0;
+                }
+                ++group_size;
+                ++gid;
+            }
+            group_starts_.push_back(cell_end_);
+        }
+    }
+
+    /// Returns the local index of the cell_group that contains a cell with
+    /// with gid.
+    /// If the cell is not on the local domain, the optional return value is
+    /// not set.
+    util::optional<cell_size_type>
+    local_group_from_gid(cell_gid_type gid) const {
+        // check if gid is a local cell
+        if (!is_local_gid(gid)) {
+            return util::nothing;
+        }
+        return gid_group_partition().index(gid);
+    }
+
+    /// Returns the gid of the first cell on the local domain.
+    cell_gid_type cell_begin() const {
+        return cell_begin_;
+    }
+
+    /// Returns one past the gid of the last cell in the local domain.
+    cell_gid_type cell_end() const {
+        return cell_end_;
+    }
+
+    /// Returns the total number of cells in the global model.
+    cell_size_type num_global_cells() const {
+        return num_global_cells_;
+    }
+
+    /// Returns the number of cells on the local domain.
+    cell_size_type num_local_cells() const {
+        return cell_end()-cell_begin();
+    }
+
+    /// Returns the number of cell groups on the local domain.
+    cell_size_type num_local_groups() const {
+        return group_kinds_.size();
+    }
+
+    /// Returns meta data for a local cell group.
+    group_range_type get_group(cell_size_type i) const {
+        return {group_starts_[i], group_starts_[i+1], group_kinds_[i]};
+    }
+
+    /// Tests whether a gid is on the local domain.
+    bool is_local_gid(cell_gid_type i) const {
+        return i>=cell_begin_ && i<cell_end_;
+    }
+
+    /// Return a partition of the cell gid over local cell groups.
+    gid_partition_type gid_group_partition() const {
+        return util::partition_view(group_starts_);
+    }
+
+    /// Returns the backend policy.
+    backend_policy backend() const {
+        return backend_policy_;
+    }
+
+private:
+
+    backend_policy backend_policy_;
+    cell_gid_type cell_begin_;
+    cell_gid_type cell_end_;
+    cell_size_type num_global_cells_;
+    std::vector<cell_size_type> group_starts_;
+    std::vector<cell_kind> group_kinds_;
+};
+
+} // namespace mc
+} // namespace nest
diff --git a/src/model.hpp b/src/model.hpp
index 91d0cd4e150a42ebfa5a62a9777e48651074c5d4..cef1da4dcd5748f2df87bda9f77e2aa02e49aa7c 100644
--- a/src/model.hpp
+++ b/src/model.hpp
@@ -13,6 +13,7 @@
 #include <cell_group.hpp>
 #include <communication/communicator.hpp>
 #include <communication/global_policy.hpp>
+#include <domain_decomposition.hpp>
 #include <mc_cell_group.hpp>
 #include <profiling/profiler.hpp>
 #include <recipe.hpp>
@@ -43,18 +44,15 @@ public:
         probe_spec probe;
     };
 
-    template <typename Iter>
-    model( const recipe& rec,
-           const util::partition_range<Iter>& groups,
-           backend_policy policy):
-        cell_group_divisions_(groups.divisions().begin(), groups.divisions().end()),
-        backend_policy_(policy)
+    model(const recipe& rec, const domain_decomposition& decomp):
+        domain_(decomp)
     {
         // set up communicator based on partition
-        communicator_ = communicator_type(gid_partition());
+        communicator_ = communicator_type(domain_.gid_group_partition());
 
         // generate the cell groups in parallel, with one task per cell group
-        cell_groups_.resize(gid_partition().size());
+        cell_groups_.resize(domain_.num_local_groups());
+
         // thread safe vector for constructing the list of probes in parallel
         threading::parallel_vector<probe_record> probe_tmp;
 
@@ -62,11 +60,11 @@ public:
             [&](cell_gid_type i) {
                 PE("setup", "cells");
 
-                auto gids = gid_partition()[i];
-                std::vector<cell> cells{gids.second-gids.first};
+                auto gids = domain_.get_group(i);
+                std::vector<cell> cells(gids.end-gids.begin);
 
-                for (auto gid: util::make_span(gids)) {
-                    auto i = gid-gids.first;
+                for (auto gid: util::make_span(gids.begin, gids.end)) {
+                    auto i = gid-gids.begin;
                     cells[i] = rec.get_cell(gid);
 
                     cell_lid_type j = 0;
@@ -76,11 +74,11 @@ public:
                     }
                 }
 
-                if (backend_policy_==backend_policy::use_multicore) {
-                    cell_groups_[i] = make_cell_group<multicore_lowered_cell>(gids.first, cells);
+                if (domain_.backend()==backend_policy::use_multicore) {
+                    cell_groups_[i] = make_cell_group<multicore_lowered_cell>(gids.begin, cells);
                 }
                 else {
-                    cell_groups_[i] = make_cell_group<gpu_lowered_cell>(gids.first, cells);
+                    cell_groups_[i] = make_cell_group<gpu_lowered_cell>(gids.begin, cells);
                 }
                 PL(2);
             });
@@ -89,7 +87,7 @@ public:
         probes_.assign(probe_tmp.begin(), probe_tmp.end());
 
         // generate the network connections
-        for (cell_gid_type i: util::make_span(gid_partition().bounds())) {
+        for (cell_gid_type i: util::make_span(domain_.cell_begin(), domain_.cell_end())) {
             for (const auto& cc: rec.connections_on(i)) {
                 connection conn{cc.source, cc.dest, cc.weight, cc.delay};
                 communicator_.add_connection(conn);
@@ -104,11 +102,6 @@ public:
         future_events().resize(num_groups());
     }
 
-    // one cell per group:
-    model(const recipe& rec, backend_policy policy):
-        model(rec, util::partition_view(util::make_span(0, rec.num_cells()+1)), policy)
-    {}
-
     void reset() {
         t_ = 0.;
         for (auto& group: cell_groups_) {
@@ -220,16 +213,18 @@ public:
 
     // only thread safe if called outside the run() method
     void add_artificial_spike(cell_member_type source, time_type tspike) {
-        current_spikes().get().push_back({source, tspike});
+        if (domain_.is_local_gid(source.gid)) {
+            current_spikes().get().push_back({source, tspike});
+        }
     }
 
     void attach_sampler(cell_member_type probe_id, sampler_function f, time_type tfrom = 0) {
-        if (!algorithms::in_interval(probe_id.gid, gid_partition().bounds())) {
-            return;
-        }
+        const auto idx = domain_.local_group_from_gid(probe_id.gid);
 
-        const auto idx = gid_partition().index(probe_id.gid);
-        cell_groups_[idx]->add_sampler(probe_id, f, tfrom);
+        // only attach samplers for local cells
+        if (idx) {
+            cell_groups_[*idx]->add_sampler(probe_id, f, tfrom);
+        }
     }
 
     const std::vector<probe_record>& probes() const { return probes_; }
@@ -243,8 +238,7 @@ public:
     }
 
     std::size_t num_cells() const {
-        auto bounds = gid_partition().bounds();
-        return bounds.second-bounds.first;
+        return domain_.num_local_cells();
     }
 
     // Set event binning policy on all our groups.
@@ -272,12 +266,7 @@ public:
     }
 
 private:
-    std::vector<cell_gid_type> cell_group_divisions_;
-    backend_policy backend_policy_;
-
-    auto gid_partition() const -> decltype(util::partition_view(cell_group_divisions_)) {
-        return util::partition_view(cell_group_divisions_);
-    }
+    const domain_decomposition &domain_;
 
     time_type t_ = 0.;
     std::vector<std::unique_ptr<cell_group>> cell_groups_;
diff --git a/src/recipe.hpp b/src/recipe.hpp
index f64c7992e33d65e87fd18588dae49f86d8718211..683ac1d6f2efb73a77ac2b8b97c0412fd57df500 100644
--- a/src/recipe.hpp
+++ b/src/recipe.hpp
@@ -4,6 +4,8 @@
 #include <memory>
 #include <stdexcept>
 
+#include <cell.hpp>
+
 namespace nest {
 namespace mc {
 
diff --git a/src/util/meta.hpp b/src/util/meta.hpp
index 52c676698844e8405897dbf240fc83382e10fbec..b1ce5f7540407844e60832b03714f93fd86b0b1b 100644
--- a/src/util/meta.hpp
+++ b/src/util/meta.hpp
@@ -187,10 +187,6 @@ struct common_random_access_iterator<
 template <typename I, typename E>
 using common_random_access_iterator_t = typename common_random_access_iterator<I, E>::type;
 
-//
-// TODO : now that we are using gcc 5+, replace these old skool SFINAE thingys
-//
-
 namespace impl {
     /// Helper for SFINAE tests that can "sink" any type
     template<typename T>
diff --git a/tests/global_communication/CMakeLists.txt b/tests/global_communication/CMakeLists.txt
index 8e66b9afc8035b5df6c96dae7cdb10f87f92c10b..6a653a415bae8d160259e6e5ee277a9482b8d23e 100644
--- a/tests/global_communication/CMakeLists.txt
+++ b/tests/global_communication/CMakeLists.txt
@@ -2,6 +2,7 @@ set(HEADERS
     ${PROJECT_SOURCE_DIR}/src/swcio.hpp
 )
 set(COMMUNICATION_SOURCES
+    test_domain_decomposition.cpp
     test_exporter_spike_file.cpp
     test_communicator.cpp
     test_mpi_gather_all.cpp
diff --git a/tests/global_communication/test_communicator.cpp b/tests/global_communication/test_communicator.cpp
index b13c8e4f9a7412456289fa2c9dd02e31313dd86d..5cbceec6b08a2f538e063b73e350010b80923460 100644
--- a/tests/global_communication/test_communicator.cpp
+++ b/tests/global_communication/test_communicator.cpp
@@ -13,7 +13,7 @@ using namespace nest::mc;
 
 using communicator_type = communication::communicator<communication::global_policy>;
 
-bool is_dry_run() {
+static bool is_dry_run() {
     return communication::global_policy::kind() ==
         communication::global_policy_kind::dryrun;
 }
diff --git a/tests/global_communication/test_domain_decomposition.cpp b/tests/global_communication/test_domain_decomposition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad0048f0cffc529faa49d2d26cc60f66da43b36b
--- /dev/null
+++ b/tests/global_communication/test_domain_decomposition.cpp
@@ -0,0 +1,30 @@
+#include "../gtest.h"
+
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <communication/communicator.hpp>
+#include <communication/global_policy.hpp>
+
+using namespace nest::mc;
+
+using communicator_type = communication::communicator<communication::global_policy>;
+
+static bool is_dry_run() {
+    return communication::global_policy::kind() ==
+        communication::global_policy_kind::dryrun;
+}
+
+TEST(domain_decomp, basic) {
+    using policy = communication::global_policy;
+
+/*
+    const auto num_domains = policy::size();
+    const auto rank = policy::id();
+*/
+
+
+}
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 0bae169cdb41837b3f1263743acd37681cfd95a2..ecae57a183301a035d4d0b4d117d2594b0eaff31 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -37,6 +37,7 @@ set(TEST_SOURCES
     test_compartments.cpp
     test_counter.cpp
     test_cycle.cpp
+    test_domain_decomposition.cpp
     test_either.cpp
     test_event_queue.cpp
     test_event_binner.cpp
diff --git a/tests/unit/test_domain_decomposition.cpp b/tests/unit/test_domain_decomposition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab5d3fe93c931031d1a1ead420cd301ea737fe48
--- /dev/null
+++ b/tests/unit/test_domain_decomposition.cpp
@@ -0,0 +1,118 @@
+#include "../gtest.h"
+
+#include <domain_decomposition.hpp>
+#include <backends.hpp>
+
+using namespace nest::mc;
+
+namespace {
+
+// dummy recipe type for testing
+class test_recipe: public recipe {
+public:
+    test_recipe(cell_size_type s): size_(s)
+    {}
+
+    cell_size_type num_cells() const override {
+        return size_;
+    }
+
+    cell get_cell(cell_gid_type) const override {
+        return cell();
+    }
+    cell_kind get_cell_kind(cell_gid_type) const override {
+        return cell_kind::cable1d_neuron;
+    }
+
+    cell_count_info get_cell_count_info(cell_gid_type) const override {
+        return {0, 0, 0};
+    }
+    std::vector<cell_connection> connections_on(cell_gid_type) const override {
+        return {};
+    }
+
+private:
+    cell_size_type size_;
+};
+
+}
+
+TEST(domain_decomposition, one_cell_groups)
+{
+    group_rules rules{1, backend_policy::use_multicore};
+
+    unsigned num_cells = 10;
+    domain_decomposition decomp(test_recipe(num_cells), rules);
+
+    EXPECT_EQ(0u, decomp.cell_begin());
+    EXPECT_EQ(num_cells, decomp.cell_end());
+
+    EXPECT_EQ(num_cells, decomp.num_local_cells());
+    EXPECT_EQ(num_cells, decomp.num_global_cells());
+    EXPECT_EQ(num_cells, decomp.num_local_groups());
+
+    // cell group indexes are monotonically increasing
+    for (unsigned i=0u; i<num_cells; ++i) {
+        auto g = decomp.get_group(i);
+        EXPECT_LT(g.begin, g.end);
+        EXPECT_EQ(g.end-g.begin, 1u);
+    }
+
+    // check that local gid are identified as local
+    for (auto i=0u; i<num_cells; ++i) {
+        EXPECT_TRUE(decomp.is_local_gid(i));
+    }
+    EXPECT_FALSE(decomp.is_local_gid(num_cells));
+}
+
+TEST(domain_decomposition, multi_cell_groups)
+{
+    unsigned num_cells = 10;
+
+    // test group sizes from 1 up to 1 more than the total number of cells
+    for (unsigned group_size=1u; group_size<=num_cells+1; ++group_size) {
+        group_rules rules{group_size, backend_policy::use_multicore};
+        domain_decomposition decomp(test_recipe(num_cells), rules);
+
+        EXPECT_EQ(0u, decomp.cell_begin());
+        EXPECT_EQ(num_cells, decomp.cell_end());
+
+        EXPECT_EQ(num_cells, decomp.num_local_cells());
+        EXPECT_EQ(num_cells, decomp.num_global_cells());
+
+        unsigned num_groups = decomp.num_local_groups();
+
+        // check that cell group indexes are monotonically increasing
+        unsigned total_cells = 0;
+        std::vector<cell_size_type> bounds{decomp.cell_begin()};
+        for (auto i=0u; i<num_groups; ++i) {
+            auto g = decomp.get_group(i);
+            auto size = g.end-g.begin;
+
+            // assert that size of the group:
+            //   is nonzero
+            EXPECT_LT(g.begin, g.end);
+            //   is no larger than group_size
+            EXPECT_TRUE(size<=group_size);
+
+            // Check that the start of this group matches the end of
+            // the preceding group.
+            EXPECT_EQ(bounds.back(), g.begin);
+            bounds.push_back(g.end);
+
+            total_cells += size;
+        }
+        // check that the sum of the group sizes euqals the total number of cells
+        EXPECT_EQ(total_cells, num_cells);
+
+        // check that local gid are identified as local
+        for (auto i=0u; i<num_cells; ++i) {
+            EXPECT_TRUE(decomp.is_local_gid(i));
+            auto group_id = decomp.local_group_from_gid(i);
+            EXPECT_TRUE(group_id);
+            EXPECT_LT(*group_id, num_groups);
+        }
+        EXPECT_FALSE(decomp.is_local_gid(num_cells));
+        EXPECT_FALSE(decomp.local_group_from_gid(num_cells));
+    }
+}
diff --git a/tests/validation/validate_ball_and_stick.hpp b/tests/validation/validate_ball_and_stick.hpp
index da1652e6747e311479b07515faca3c799df8d923..786c6bc7b8c05592f81fb350a6d5e9c49b430ec8 100644
--- a/tests/validation/validate_ball_and_stick.hpp
+++ b/tests/validation/validate_ball_and_stick.hpp
@@ -49,7 +49,8 @@ void run_ncomp_convergence_test(
                 seg->set_compartments(ncomp);
             }
         }
-        model m(singleton_recipe{c}, backend);
+        domain_decomposition decomp(singleton_recipe{c}, {1u, backend});
+        model m(singleton_recipe{c}, decomp);
 
         runner.run(m, ncomp, t_end, dt, exclude);
     }
diff --git a/tests/validation/validate_kinetic.hpp b/tests/validation/validate_kinetic.hpp
index 535ed8697abf985893a00108435c51897575c332..ba21d0697a5bce98b3cb30643576fbee489f6f24 100644
--- a/tests/validation/validate_kinetic.hpp
+++ b/tests/validation/validate_kinetic.hpp
@@ -32,7 +32,8 @@ void run_kinetic_dt(
     convergence_test_runner<float> runner("dt", samplers, meta);
     runner.load_reference_data(ref_file);
 
-    model model(singleton_recipe{c}, backend);
+    domain_decomposition decomp(singleton_recipe{c}, {1u, backend});
+    model model(singleton_recipe{c}, decomp);
 
     auto exclude = stimulus_ends(c);
 
diff --git a/tests/validation/validate_soma.hpp b/tests/validation/validate_soma.hpp
index 0c533faa044c9bf84bcfbbd279b77fbddcdc5846..862e50304aaa776302f913fd3156772eb861d95e 100644
--- a/tests/validation/validate_soma.hpp
+++ b/tests/validation/validate_soma.hpp
@@ -18,7 +18,8 @@ void validate_soma(nest::mc::backend_policy backend) {
 
     cell c = make_cell_soma_only();
     add_common_voltage_probes(c);
-    model model(singleton_recipe{c}, backend);
+    domain_decomposition decomp(singleton_recipe{c}, {1u, backend});
+    model m(singleton_recipe{c}, decomp);
 
     float sample_dt = .025f;
     sampler_info samplers[] = {{"soma.mid", {0u, 0u}, simple_sampler(sample_dt)}};
@@ -43,9 +44,9 @@ void validate_soma(nest::mc::backend_policy backend) {
             double oo_dt = base/multiple;
             if (oo_dt>max_oo_dt) goto end;
 
-            model.reset();
+            m.reset();
             float dt = float(1./oo_dt);
-            runner.run(model, dt, t_end, dt, {});
+            runner.run(m, dt, t_end, dt, {});
         }
     }
 end:
diff --git a/tests/validation/validate_synapses.hpp b/tests/validation/validate_synapses.hpp
index c3f7f29413526747a982c62733b4e6ae08862969..83a77d36eb73bfee8533055a90cc1c872aecc38b 100644
--- a/tests/validation/validate_synapses.hpp
+++ b/tests/validation/validate_synapses.hpp
@@ -60,7 +60,8 @@ void run_synapse_test(
 
     for (int ncomp = 10; ncomp<max_ncomp; ncomp*=2) {
         c.cable(1)->set_compartments(ncomp);
-        model m(singleton_recipe{c}, backend);
+        domain_decomposition decomp(singleton_recipe{c}, {1u, backend});
+        model m(singleton_recipe{c}, decomp);
         m.group(0).enqueue_events(synthetic_events);
 
         runner.run(m, ncomp, t_end, dt, exclude);