From a1894edc929111fe95e3f881eb80dab8e3ba2387 Mon Sep 17 00:00:00 2001
From: Sam Yates <yates@cscs.ch>
Date: Tue, 3 Jul 2018 09:39:59 +0200
Subject: [PATCH] Move cell description types to public includes. (#508)

Further work to public install target.

* Move SIMD classes, cell description classes, simple sampler to public include.
* Rename `cell` to `mc_cell`, `segment` to `mc_segment`, and remove `_description` from cell description class names and includes.
* Move `compartment_model` out of `mc_cell` interface and use only in `fvm_layout.cpp`.
* (Provisionally) remove area/volume methods on `mc_cell` and `mc_segment`.
---
 arbor/CMakeLists.txt                          |   2 +-
 arbor/backends/multicore/multicore_common.hpp |   2 -
 .../multicore/partition_by_constraint.hpp     |   5 +-
 arbor/backends/multicore/shared_state.cpp     |   4 +-
 arbor/backends/multicore/shared_state.hpp     |   4 +-
 arbor/benchmark_cell_group.cpp                |   7 +-
 arbor/benchmark_cell_group.hpp                |   9 +-
 arbor/cell_group.hpp                          |   3 +-
 arbor/cell_group_factory.cpp                  |   1 -
 arbor/cell_group_factory.hpp                  |  11 +-
 arbor/communication/communicator.hpp          |   4 +-
 arbor/event_generator.hpp                     |   2 +-
 .../{compartment.hpp => fvm_compartment.hpp}  |  71 +-----
 arbor/fvm_layout.cpp                          |  43 +++-
 arbor/fvm_layout.hpp                          |   8 +-
 arbor/fvm_lowered_cell_impl.hpp               |   7 +-
 arbor/lif_cell_group.cpp                      |   8 +-
 arbor/lif_cell_group.hpp                      |  17 +-
 arbor/{cell.cpp => mc_cell.cpp}               | 104 +++------
 arbor/mc_cell_group.cpp                       |  10 +-
 arbor/mc_cell_group.hpp                       |   4 +-
 arbor/mechcat.cpp                             |   2 +-
 arbor/merge_events.cpp                        |  32 +--
 arbor/merge_events.hpp                        |   3 +-
 arbor/morphology.cpp                          |   4 +-
 arbor/partition_load_balance.cpp              |   3 +-
 arbor/recipe.hpp                              |   3 +-
 arbor/sampler_map.hpp                         |   2 +-
 arbor/simulation.cpp                          |  23 +-
 arbor/simulation.hpp                          |   2 +-
 arbor/spike_source_cell_group.cpp             |  14 +-
 arbor/spike_source_cell_group.hpp             |   7 +-
 arbor/swcio.cpp                               |   9 +-
 arbor/swcio.hpp                               |   6 +-
 example/bench/recipe.cpp                      |   5 +-
 example/brunel/brunel_miniapp.cpp             |   6 +-
 example/generators/event_gen.cpp              |   6 +-
 example/miniapp/miniapp.cpp                   |   8 +-
 example/miniapp/miniapp_recipes.cpp           |  12 +-
 example/miniapp/morphology_pool.cpp           |   7 +-
 example/miniapp/morphology_pool.hpp           |   5 +-
 example/miniapp/trace.hpp                     |   3 +-
 {arbor => include/arbor}/benchmark_cell.hpp   |   2 +-
 {arbor => include/arbor}/constants.hpp        |   0
 .../arbor/lif_cell.hpp                        |   2 +-
 arbor/cell.hpp => include/arbor/mc_cell.hpp   |  58 ++---
 .../arbor/mc_segment.hpp                      | 121 +++-------
 {arbor => include/arbor}/morphology.hpp       |   0
 {arbor => include/arbor}/point.hpp            |   0
 {arbor => include/arbor}/sampling.hpp         |   3 +-
 {arbor => include/arbor}/simd/approx.hpp      |   0
 {arbor => include/arbor}/simd/avx.hpp         |   4 +-
 {arbor => include/arbor}/simd/avx512.hpp      |   4 +-
 {arbor => include/arbor}/simd/generic.hpp     |   2 +-
 {arbor => include/arbor}/simd/implbase.hpp    |   0
 {arbor => include/arbor}/simd/native.hpp      |   0
 {arbor => include/arbor}/simd/simd.hpp        |   6 +-
 {arbor => include/arbor}/simd/simd_io.hpp     |   2 +-
 {arbor => include/arbor}/simple_sampler.hpp   |  35 +--
 .../arbor}/spike_source_cell.hpp              |   2 +-
 {arbor => include/arbor}/time_sequence.hpp    |  12 +-
 {arbor => include/arbor}/util/any.hpp         |  10 +-
 {arbor => include/arbor}/util/any_ptr.hpp     |   0
 {arbor => include/arbor}/util/make_unique.hpp |   4 +-
 {arbor => include/arbor}/util/unique_any.hpp  |  16 +-
 lmorpho/lmorpho.cpp                           |   2 +-
 lmorpho/lsystem.cpp                           |   5 +-
 lmorpho/lsystem.hpp                           |   2 +-
 lmorpho/morphio.cpp                           |   7 +-
 lmorpho/morphio.hpp                           |   2 +-
 modcc/printer/cprinter.cpp                    |   2 +-
 test/common_cells.hpp                         |  35 ++-
 test/simple_recipes.hpp                       |  11 +-
 test/ubench/mech_vec.cpp                      |  15 +-
 test/unit/CMakeLists.txt                      |   2 +-
 test/unit/test_any.cpp                        |  10 +-
 test/unit/test_compartments.cpp               |  89 ++------
 test/unit/test_domain_decomposition.cpp       |  12 +-
 test/unit/test_fvm_layout.cpp                 | 209 +++++++++++-------
 test/unit/test_fvm_lowered.cpp                |  18 +-
 test/unit/test_lif_cell_group.cpp             |  19 +-
 test/unit/{test_cell.cpp => test_mc_cell.cpp} |  86 +++----
 test/unit/test_mc_cell_group.cpp              |   4 +-
 test/unit/test_mc_cell_group_gpu.cpp          |   2 +-
 test/unit/test_mechinfo.cpp                   |   2 +-
 test/unit/test_partition_by_constraint.cpp    |   6 +-
 test/unit/test_point.cpp                      |   2 +-
 test/unit/test_probe.cpp                      |  12 +-
 test/unit/test_segment.cpp                    |  44 +---
 test/unit/test_simd.cpp                       |   4 +-
 test/unit/test_spike_source.cpp               |   9 +-
 test/unit/test_swcio.cpp                      |  54 +++--
 test/unit/test_synapses.cpp                   |  16 +-
 test/unit/test_time_seq.cpp                   |   6 +-
 test/unit/test_unique_any.cpp                 |   7 +-
 test/validation/convergence_test.hpp          |  14 +-
 test/validation/trace_analysis.cpp            |   8 +-
 test/validation/trace_analysis.hpp            |  32 ++-
 test/validation/validate_ball_and_stick.cpp   |  34 +--
 .../validate_compartment_policy.cpp           |   4 +-
 test/validation/validate_kinetic.cpp          |  12 +-
 test/validation/validate_soma.cpp             |   6 +-
 test/validation/validate_synapses.cpp         |  20 +-
 test/validation/validation_data.cpp           |   6 +-
 test/validation/validation_data.hpp           |   5 +-
 105 files changed, 709 insertions(+), 897 deletions(-)
 rename arbor/{compartment.hpp => fvm_compartment.hpp} (78%)
 rename arbor/{cell.cpp => mc_cell.cpp} (51%)
 rename {arbor => include/arbor}/benchmark_cell.hpp (93%)
 rename {arbor => include/arbor}/constants.hpp (100%)
 rename arbor/lif_cell_description.hpp => include/arbor/lif_cell.hpp (94%)
 rename arbor/cell.hpp => include/arbor/mc_cell.hpp (85%)
 rename arbor/segment.hpp => include/arbor/mc_segment.hpp (74%)
 rename {arbor => include/arbor}/morphology.hpp (100%)
 rename {arbor => include/arbor}/point.hpp (100%)
 rename {arbor => include/arbor}/sampling.hpp (95%)
 rename {arbor => include/arbor}/simd/approx.hpp (100%)
 rename {arbor => include/arbor}/simd/avx.hpp (99%)
 rename {arbor => include/arbor}/simd/avx512.hpp (99%)
 rename {arbor => include/arbor}/simd/generic.hpp (97%)
 rename {arbor => include/arbor}/simd/implbase.hpp (100%)
 rename {arbor => include/arbor}/simd/native.hpp (100%)
 rename {arbor => include/arbor}/simd/simd.hpp (99%)
 rename {arbor => include/arbor}/simd/simd_io.hpp (94%)
 rename {arbor => include/arbor}/simple_sampler.hpp (53%)
 rename {arbor => include/arbor}/spike_source_cell.hpp (87%)
 rename {arbor => include/arbor}/time_sequence.hpp (96%)
 rename {arbor => include/arbor}/util/any.hpp (94%)
 rename {arbor => include/arbor}/util/any_ptr.hpp (100%)
 rename {arbor => include/arbor}/util/make_unique.hpp (74%)
 rename {arbor => include/arbor}/util/unique_any.hpp (91%)
 rename test/unit/{test_cell.cpp => test_mc_cell.cpp} (80%)

diff --git a/arbor/CMakeLists.txt b/arbor/CMakeLists.txt
index 4df24314..8208a98e 100644
--- a/arbor/CMakeLists.txt
+++ b/arbor/CMakeLists.txt
@@ -9,7 +9,7 @@ set(arbor_sources
     builtin_mechanisms.cpp
     cell_group_factory.cpp
     common_types_io.cpp
-    cell.cpp
+    mc_cell.cpp
     event_binner.cpp
     fvm_layout.cpp
     fvm_lowered_cell_impl.cpp
diff --git a/arbor/backends/multicore/multicore_common.hpp b/arbor/backends/multicore/multicore_common.hpp
index f2ef70ec..b0b3ed4e 100644
--- a/arbor/backends/multicore/multicore_common.hpp
+++ b/arbor/backends/multicore/multicore_common.hpp
@@ -11,8 +11,6 @@
 #include <arbor/fvm_types.hpp>
 
 #include "backends/event.hpp"
-#include "math.hpp"
-#include "simd/simd.hpp"
 #include "util/padded_alloc.hpp"
 
 #include "multi_event_stream.hpp"
diff --git a/arbor/backends/multicore/partition_by_constraint.hpp b/arbor/backends/multicore/partition_by_constraint.hpp
index b4cf0c06..dc7d4d92 100644
--- a/arbor/backends/multicore/partition_by_constraint.hpp
+++ b/arbor/backends/multicore/partition_by_constraint.hpp
@@ -1,7 +1,8 @@
 #pragma once
 
-#include<vector>
-#include<simd/simd.hpp>
+#include <vector>
+
+#include <arbor/simd/simd.hpp>
 
 namespace arb {
 namespace multicore {
diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp
index 00848199..2af16850 100644
--- a/arbor/backends/multicore/shared_state.cpp
+++ b/arbor/backends/multicore/shared_state.cpp
@@ -6,15 +6,15 @@
 #include <vector>
 
 #include <arbor/assert.hpp>
+#include <arbor/constants.hpp>
 #include <arbor/fvm_types.hpp>
 #include <arbor/common_types.hpp>
 #include <arbor/ion.hpp>
+#include <arbor/simd/simd.hpp>
 
 #include "backends/event.hpp"
-#include "constants.hpp"
 #include "io/sepval.hpp"
 #include "math.hpp"
-#include "simd/simd.hpp"
 #include "util/padded_alloc.hpp"
 #include "util/rangeutil.hpp"
 
diff --git a/arbor/backends/multicore/shared_state.hpp b/arbor/backends/multicore/shared_state.hpp
index 3188dade..af2b19be 100644
--- a/arbor/backends/multicore/shared_state.hpp
+++ b/arbor/backends/multicore/shared_state.hpp
@@ -11,13 +11,11 @@
 #include <arbor/common_types.hpp>
 #include <arbor/fvm_types.hpp>
 #include <arbor/ion.hpp>
+#include <arbor/simd/simd.hpp>
 #include <arbor/util/enumhash.hpp>
 
 #include "backends/event.hpp"
-#include "constants.hpp"
 #include "event_queue.hpp"
-#include "math.hpp"
-#include "simd/simd.hpp"
 #include "util/padded_alloc.hpp"
 #include "util/rangeutil.hpp"
 
diff --git a/arbor/benchmark_cell_group.cpp b/arbor/benchmark_cell_group.cpp
index d89dacd2..6466d7c1 100644
--- a/arbor/benchmark_cell_group.cpp
+++ b/arbor/benchmark_cell_group.cpp
@@ -1,12 +1,15 @@
 #include <chrono>
 #include <exception>
 
+#include <arbor/benchmark_cell.hpp>
+#include <arbor/time_sequence.hpp>
+
 #include <cell_group.hpp>
 #include <profile/profiler_macro.hpp>
 #include <recipe.hpp>
-#include <benchmark_cell.hpp>
 #include <benchmark_cell_group.hpp>
-#include <time_sequence.hpp>
+
+#include "util/span.hpp"
 
 namespace arb {
 
diff --git a/arbor/benchmark_cell_group.hpp b/arbor/benchmark_cell_group.hpp
index 536a7fa1..146baf7f 100644
--- a/arbor/benchmark_cell_group.hpp
+++ b/arbor/benchmark_cell_group.hpp
@@ -1,9 +1,10 @@
 #pragma once
 
-#include <benchmark_cell.hpp>
-#include <cell_group.hpp>
-#include <recipe.hpp>
-#include <time_sequence.hpp>
+#include <arbor/benchmark_cell.hpp>
+#include <arbor/time_sequence.hpp>
+
+#include "cell_group.hpp"
+#include "recipe.hpp"
 
 namespace arb {
 
diff --git a/arbor/cell_group.hpp b/arbor/cell_group.hpp
index 3a3d3035..ed53924f 100644
--- a/arbor/cell_group.hpp
+++ b/arbor/cell_group.hpp
@@ -5,13 +5,12 @@
 #include <vector>
 
 #include <arbor/common_types.hpp>
+#include <arbor/sampling.hpp>
 #include <arbor/spike.hpp>
 
-#include "cell.hpp"
 #include "epoch.hpp"
 #include "event_binner.hpp"
 #include "event_queue.hpp"
-#include "sampling.hpp"
 #include "schedule.hpp"
 
 namespace arb {
diff --git a/arbor/cell_group_factory.cpp b/arbor/cell_group_factory.cpp
index 4c613695..96079522 100644
--- a/arbor/cell_group_factory.cpp
+++ b/arbor/cell_group_factory.cpp
@@ -9,7 +9,6 @@
 #include <mc_cell_group.hpp>
 #include <recipe.hpp>
 #include <spike_source_cell_group.hpp>
-#include <util/unique_any.hpp>
 
 namespace arb {
 
diff --git a/arbor/cell_group_factory.hpp b/arbor/cell_group_factory.hpp
index 12c038b1..88770475 100644
--- a/arbor/cell_group_factory.hpp
+++ b/arbor/cell_group_factory.hpp
@@ -2,11 +2,12 @@
 
 #include <vector>
 
-#include <backends.hpp>
-#include <cell_group.hpp>
-#include <domain_decomposition.hpp>
-#include <recipe.hpp>
-#include <util/unique_any.hpp>
+#include <arbor/util/unique_any.hpp>
+
+#include "backends.hpp"
+#include "cell_group.hpp"
+#include "domain_decomposition.hpp"
+#include "recipe.hpp"
 
 namespace arb {
 
diff --git a/arbor/communication/communicator.hpp b/arbor/communication/communicator.hpp
index 60c7d5cb..d9a6f759 100644
--- a/arbor/communication/communicator.hpp
+++ b/arbor/communication/communicator.hpp
@@ -23,6 +23,7 @@
 #include "util/double_buffer.hpp"
 #include "util/partition.hpp"
 #include "util/rangeutil.hpp"
+#include "util/span.hpp"
 
 namespace arb {
 
@@ -45,7 +46,6 @@ public:
                           const domain_decomposition& dom_dec,
                           const distributed_context* ctx)
     {
-        using util::make_span;
         context_ = ctx;
         num_domains_ = context_->size();
         num_local_groups_ = dom_dec.groups.size();
@@ -186,7 +186,7 @@ public:
 
         const auto& sp = global_spikes.partition();
         const auto& cp = connection_part_;
-        for (auto dom: make_span(0, num_domains_)) {
+        for (auto dom: make_span(num_domains_)) {
             auto cons = subrange_view(connections_, cp[dom], cp[dom+1]);
             auto spks = subrange_view(global_spikes.values(), sp[dom], sp[dom+1]);
 
diff --git a/arbor/event_generator.hpp b/arbor/event_generator.hpp
index 6b4fc482..048b62eb 100644
--- a/arbor/event_generator.hpp
+++ b/arbor/event_generator.hpp
@@ -5,9 +5,9 @@
 #include <random>
 
 #include <arbor/common_types.hpp>
+#include <arbor/time_sequence.hpp>
 
 #include "event_queue.hpp"
-#include "time_sequence.hpp"
 #include "util/range.hpp"
 #include "util/rangeutil.hpp"
 
diff --git a/arbor/compartment.hpp b/arbor/fvm_compartment.hpp
similarity index 78%
rename from arbor/compartment.hpp
rename to arbor/fvm_compartment.hpp
index fa220ccf..6361c511 100644
--- a/arbor/compartment.hpp
+++ b/arbor/fvm_compartment.hpp
@@ -6,84 +6,15 @@
 #include <arbor/common_types.hpp>
 #include <arbor/util/compat.hpp>
 
+#include "algorithms.hpp"
 #include "math.hpp"
-#include "util/counter.hpp"
 #include "util/iterutil.hpp"
 #include "util/partition.hpp"
-#include "util/span.hpp"
 #include "util/rangeutil.hpp"
 #include "util/transform.hpp"
 
 namespace arb {
 
-/// Defines the simplest type of compartment
-/// The compartment is a conic frustrum
-struct compartment {
-    using value_type = double;
-    using size_type = cell_local_size_type;
-    using value_pair = std::pair<value_type, value_type>;
-
-    compartment() = delete;
-
-    compartment(
-        size_type idx,
-        value_type len,
-        value_type r1,
-        value_type r2
-    )
-    :   index{idx},
-        radius{r1, r2},
-        length{len}
-    {}
-
-
-    size_type index;
-    std::pair<value_type, value_type> radius;
-    value_type length;
-};
-
-// (NB: auto type deduction and lambda in C++14 will simplify the following)
-
-template <typename size_type, typename real_type>
-class compartment_maker {
-public:
-    compartment_maker(size_type n, real_type length, real_type rL, real_type rR):
-        r0_{rL},
-        dr_{(rR-rL)/n},
-        dx_{length/n}
-    {}
-
-    compartment operator()(size_type i) const {
-        return compartment(i, dx_, r0_+i*dr_, r0_+(i+1)*dr_);
-    }
-
-private:
-    real_type r0_;
-    real_type dr_;
-    real_type dx_;
-};
-
-template <typename size_type, typename real_type>
-using compartment_iterator =
-    util::transform_iterator<util::counter<size_type>, compartment_maker<size_type, real_type>>;
-
-template <typename size_type, typename real_type>
-using compartment_range =
-    util::range<compartment_iterator<size_type, real_type>>;
-
-
-template <typename size_type, typename real_type>
-compartment_range<size_type, real_type> make_compartment_range(
-    size_type num_compartments,
-    real_type radius_L,
-    real_type radius_R,
-    real_type length)
-{
-    return util::transform_view(
-        util::span<size_type>(0, num_compartments),
-        compartment_maker<size_type, real_type>(num_compartments, length, radius_L, radius_R));
-}
-
 /// Divided compartments for use with (e.g.) fvm control volume setup
 
 struct semi_compartment {
diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp
index 12b5d7ca..d634fc71 100644
--- a/arbor/fvm_layout.cpp
+++ b/arbor/fvm_layout.cpp
@@ -3,9 +3,13 @@
 #include <unordered_set>
 #include <vector>
 
+#include <arbor/mc_cell.hpp>
 #include <arbor/util/enumhash.hpp>
 
+#include "algorithms.hpp"
+#include "fvm_compartment.hpp"
 #include "fvm_layout.hpp"
+#include "tree.hpp"
 #include "util/maputil.hpp"
 #include "util/meta.hpp"
 #include "util/partition.hpp"
@@ -22,12 +26,27 @@ using util::value_by_key;
 
 // Convenience routines
 
-template <typename ResizableContainer, typename Index>
-void extend_to(ResizableContainer& c, const Index& i) {
-    if (util::size(c)<=i) {
-        c.resize(i+1);
+namespace {
+    template <typename ResizableContainer, typename Index>
+    void extend_to(ResizableContainer& c, const Index& i) {
+        if (util::size(c)<=i) {
+            c.resize(i+1);
+        }
     }
-}
+
+    struct compartment_model {
+        arb::tree tree;
+        std::vector<tree::int_type> parent_index;
+        std::vector<tree::int_type> segment_index;
+
+        explicit compartment_model(const mc_cell& cell) {
+            tree = arb::tree(cell.parents());
+            auto counts = cell.compartment_counts();
+            parent_index = make_parent_index(tree, counts);
+            segment_index = algorithms::make_index(counts);
+        }
+    };
+} // namespace
 
 // Cable segment discretization
 // ----------------------------
@@ -91,7 +110,7 @@ void extend_to(ResizableContainer& c, const Index& i) {
 //       = 1/R · hV₁V₂/(h₂²V₁+h₁²V₂)
 //
 
-fvm_discretization fvm_discretize(const std::vector<cell>& cells) {
+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;
@@ -99,11 +118,11 @@ fvm_discretization fvm_discretize(const std::vector<cell>& cells) {
     fvm_discretization D;
 
     util::make_partition(D.cell_segment_bounds,
-        transform_view(cells, [](const cell& c) { return c.num_segments(); }));
+        transform_view(cells, [](const mc_cell& c) { return c.num_segments(); }));
 
     std::vector<index_type> cell_comp_bounds;
     auto cell_comp_part = make_partition(cell_comp_bounds,
-        transform_view(cells, [](const cell& c) { return c.num_compartments(); }));
+        transform_view(cells, [](const mc_cell& c) { return c.num_compartments(); }));
 
     D.ncell = cells.size();
     D.ncomp = cell_comp_part.bounds().second;
@@ -120,7 +139,7 @@ fvm_discretization fvm_discretize(const std::vector<cell>& cells) {
     std::vector<size_type> seg_comp_bounds;
     for (auto i: make_span(0, D.ncell)) {
         const auto& c = cells[i];
-        auto cell_graph = c.model();
+        compartment_model cell_graph (c);
         auto cell_comp_ival = cell_comp_part[i];
 
         auto cell_comp_base = cell_comp_ival.first;
@@ -132,7 +151,7 @@ fvm_discretization fvm_discretize(const std::vector<cell>& cells) {
         seg_comp_bounds.clear();
         auto seg_comp_part = make_partition(
             seg_comp_bounds,
-            transform_view(c.segments(), [](const segment_ptr& s) { return s->num_compartments(); }),
+            transform_view(c.segments(), [](const mc_segment_ptr& s) { return s->num_compartments(); }),
             cell_comp_base);
 
         const auto nseg = seg_comp_part.size();
@@ -176,7 +195,7 @@ fvm_discretization fvm_discretize(const std::vector<cell>& cells) {
             auto cm = cable->cm;    // [F/m²]
             auto rL = cable->rL;    // [Ω·cm]
 
-            auto divs = div_compartments<div_compartment_integrator>(cable, ncomp);
+            auto divs = div_compartment_integrator(ncomp, cable->radii(), cable->lengths());
 
             seg_info.parent_cv = D.parent_cv[seg_comp_ival.first];
             seg_info.parent_cv_area = divs(0).left.area;
@@ -230,7 +249,7 @@ fvm_discretization fvm_discretize(const std::vector<cell>& cells) {
 //       IIb. Density mechanism CVs, parameter values; ion channel default concentration contributions.
 //       IIc. Point mechanism CVs, parameter values, and targets.
 
-fvm_mechanism_data fvm_build_mechanism_data(const mechanism_catalogue& catalogue, const std::vector<cell>& cells, const fvm_discretization& D) {
+fvm_mechanism_data fvm_build_mechanism_data(const mechanism_catalogue& catalogue, const std::vector<mc_cell>& cells, const fvm_discretization& D) {
     using util::assign;
     using util::sort_by;
     using util::optional;
diff --git a/arbor/fvm_layout.hpp b/arbor/fvm_layout.hpp
index c23d0372..9d09e5ca 100644
--- a/arbor/fvm_layout.hpp
+++ b/arbor/fvm_layout.hpp
@@ -1,13 +1,13 @@
 #pragma once
 
 #include <arbor/fvm_types.hpp>
+#include <arbor/mc_cell.hpp>
 #include <arbor/mechanism.hpp>
 #include <arbor/mechinfo.hpp>
 #include <arbor/mechcat.hpp>
 #include <arbor/util/enumhash.hpp>
 
-#include "cell.hpp"
-#include "compartment.hpp"
+#include "fvm_compartment.hpp"
 #include "util/deduce_return.hpp"
 #include "util/span.hpp"
 
@@ -86,7 +86,7 @@ struct fvm_discretization {
     }
 };
 
-fvm_discretization fvm_discretize(const std::vector<cell>& cells);
+fvm_discretization fvm_discretize(const std::vector<mc_cell>& cells);
 
 
 // Post-discretization data for point and density mechanism instantiation.
@@ -136,6 +136,6 @@ struct fvm_mechanism_data {
     std::size_t ntarget = 0;
 };
 
-fvm_mechanism_data fvm_build_mechanism_data(const mechanism_catalogue& catalogue, const std::vector<cell>& cells, const fvm_discretization& D);
+fvm_mechanism_data fvm_build_mechanism_data(const mechanism_catalogue& catalogue, const std::vector<mc_cell>& cells, const fvm_discretization& D);
 
 } // namespace arb
diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp
index 11e9f535..f8a7f6b9 100644
--- a/arbor/fvm_lowered_cell_impl.hpp
+++ b/arbor/fvm_lowered_cell_impl.hpp
@@ -24,6 +24,7 @@
 #include "profile/profiler_macro.hpp"
 #include "recipe.hpp"
 #include "sampler_map.hpp"
+#include "util/maputil.hpp"
 #include "util/meta.hpp"
 #include "util/range.hpp"
 #include "util/rangeutil.hpp"
@@ -292,16 +293,16 @@ void fvm_lowered_cell_impl<B>::initialize(
     using util::value_by_key;
     using util::keys;
 
-    std::vector<cell> cells;
+    std::vector<mc_cell> cells;
     const std::size_t ncell = gids.size();
 
     cells.reserve(ncell);
     for (auto gid: gids) {
-        cells.push_back(any_cast<cell>(rec.get_cell_description(gid)));
+        cells.push_back(any_cast<mc_cell>(rec.get_cell_description(gid)));
     }
 
     auto rec_props = rec.get_global_properties(cell_kind::cable1d_neuron);
-    auto global_props = rec_props.has_value()? any_cast<cell_global_properties>(rec_props): cell_global_properties{};
+    auto global_props = rec_props.has_value()? any_cast<mc_cell_global_properties>(rec_props): mc_cell_global_properties{};
 
     const mechanism_catalogue* catalogue = global_props.catalogue;
     initial_voltage_ = global_props.init_membrane_potential_mV;
diff --git a/arbor/lif_cell_group.cpp b/arbor/lif_cell_group.cpp
index e8b282b6..70f51250 100644
--- a/arbor/lif_cell_group.cpp
+++ b/arbor/lif_cell_group.cpp
@@ -1,5 +1,7 @@
 #include <lif_cell_group.hpp>
 
+#include "util/span.hpp"
+
 using namespace arb;
 
 // Constructor containing gid of first cell in a group and a container of all cells.
@@ -12,8 +14,8 @@ gids_(std::move(gids))
     cells_.reserve(gids_.size());
     last_time_updated_.resize(gids_.size());
 
-    for (auto lid: util::make_span(0, gids_.size())) {
-        cells_.push_back(util::any_cast<lif_cell_description>(rec.get_cell_description(gids_[lid])));
+    for (auto lid: util::make_span(gids_.size())) {
+        cells_.push_back(util::any_cast<lif_cell>(rec.get_cell_description(gids_[lid])));
     }
 }
 
@@ -24,7 +26,7 @@ cell_kind lif_cell_group::get_cell_kind() const {
 void lif_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& event_lanes) {
     PE(advance_lif);
     if (event_lanes.size() > 0) {
-        for (auto lid: util::make_span(0, gids_.size())) {
+        for (auto lid: util::make_span(gids_.size())) {
             // Advance each cell independently.
             advance_cell(ep.tfinal, dt, lid, event_lanes[lid]);
         }
diff --git a/arbor/lif_cell_group.hpp b/arbor/lif_cell_group.hpp
index d4582a8a..d81aaf3b 100644
--- a/arbor/lif_cell_group.hpp
+++ b/arbor/lif_cell_group.hpp
@@ -1,13 +1,14 @@
 #pragma once
-#include <algorithm>
-#include <cell_group.hpp>
-#include <event_queue.hpp>
-#include <lif_cell_description.hpp>
-#include "profile/profiler_macro.hpp"
-#include <recipe.hpp>
-#include <util/unique_any.hpp>
+
 #include <vector>
 
+#include <arbor/lif_cell.hpp>
+
+#include "cell_group.hpp"
+#include "event_queue.hpp"
+#include "profile/profiler_macro.hpp"
+#include "recipe.hpp"
+
 namespace arb {
 
 class lif_cell_group: public cell_group {
@@ -42,7 +43,7 @@ private:
     std::vector<cell_gid_type> gids_;
 
     // Cells that belong to this group.
-    std::vector<lif_cell_description> cells_;
+    std::vector<lif_cell> cells_;
 
     // Spikes that are generated (not necessarily sorted).
     std::vector<spike> spikes_;
diff --git a/arbor/cell.cpp b/arbor/mc_cell.cpp
similarity index 51%
rename from arbor/cell.cpp
rename to arbor/mc_cell.cpp
index 2058e78f..2184134c 100644
--- a/arbor/cell.cpp
+++ b/arbor/mc_cell.cpp
@@ -1,23 +1,28 @@
-#include <cell.hpp>
-#include <morphology.hpp>
-#include <tree.hpp>
-#include <util/rangeutil.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/mc_segment.hpp>
+#include <arbor/morphology.hpp>
+
+#include "util/rangeutil.hpp"
 
 namespace arb {
 
-cell::cell() {
+using value_type = mc_cell::value_type;
+using index_type = mc_cell::index_type;
+using size_type = mc_cell::size_type;
+
+mc_cell::mc_cell() {
     // insert a placeholder segment for the soma
     segments_.push_back(make_segment<placeholder_segment>());
     parents_.push_back(0);
 }
 
-void cell::assert_valid_segment(index_type i) const {
+void mc_cell::assert_valid_segment(index_type i) const {
     if (i>=num_segments()) {
         throw std::out_of_range("no such segment");
     }
 }
 
-cell::size_type cell::num_segments() const {
+size_type mc_cell::num_segments() const {
     return segments_.size();
 }
 
@@ -25,7 +30,7 @@ cell::size_type cell::num_segments() const {
 // note: I think that we have to enforce that the soma is the first
 //       segment that is added
 //
-soma_segment* cell::add_soma(value_type radius, point_type center) {
+soma_segment* mc_cell::add_soma(value_type radius, point_type center) {
     if (has_soma()) {
         throw std::runtime_error("cell already has soma");
     }
@@ -33,7 +38,7 @@ soma_segment* cell::add_soma(value_type radius, point_type center) {
     return segments_[0]->as_soma();
 }
 
-cable_segment* cell::add_cable(cell::index_type parent, segment_ptr&& cable) {
+cable_segment* mc_cell::add_cable(index_type parent, mc_segment_ptr&& cable) {
     if (!cable->as_cable()) {
         throw std::invalid_argument("segment is not a cable segment");
     }
@@ -48,45 +53,35 @@ cable_segment* cell::add_cable(cell::index_type parent, segment_ptr&& cable) {
     return segments_.back()->as_cable();
 }
 
-segment* cell::segment(index_type index) {
+mc_segment* mc_cell::segment(index_type index) {
     assert_valid_segment(index);
     return segments_[index].get();
 }
 
-segment const* cell::segment(index_type index) const {
+mc_segment const* mc_cell::segment(index_type index) const {
     assert_valid_segment(index);
     return segments_[index].get();
 }
 
-bool cell::has_soma() const {
+bool mc_cell::has_soma() const {
     return !segment(0)->is_placeholder();
 }
 
-soma_segment* cell::soma() {
+soma_segment* mc_cell::soma() {
     return has_soma()? segment(0)->as_soma(): nullptr;
 }
 
-const soma_segment* cell::soma() const {
+const soma_segment* mc_cell::soma() const {
     return has_soma()? segment(0)->as_soma(): nullptr;
 }
 
-cable_segment* cell::cable(index_type index) {
+cable_segment* mc_cell::cable(index_type index) {
     assert_valid_segment(index);
     auto cable = segment(index)->as_cable();
     return cable? cable: throw std::runtime_error("segment is not a cable segment");
 }
 
-cell::value_type cell::volume() const {
-    return util::sum_by(segments_,
-            [](const segment_ptr& s) { return s->volume(); });
-}
-
-cell::value_type cell::area() const {
-    return util::sum_by(segments_,
-            [](const segment_ptr& s) { return s->area(); });
-}
-
-std::vector<cell::size_type> cell::compartment_counts() const {
+std::vector<size_type> mc_cell::compartment_counts() const {
     std::vector<size_type> comp_count;
     comp_count.reserve(num_segments());
     for (const auto& s: segments()) {
@@ -95,67 +90,26 @@ std::vector<cell::size_type> cell::compartment_counts() const {
     return comp_count;
 }
 
-cell::size_type cell::num_compartments() const {
+size_type mc_cell::num_compartments() const {
     return util::sum_by(segments_,
-            [](const segment_ptr& s) { return s->num_compartments(); });
-}
-
-compartment_model cell::model() const {
-    compartment_model m;
-
-    m.tree = tree(parents_);
-    auto counts = compartment_counts();
-    m.parent_index = make_parent_index(m.tree, counts);
-    m.segment_index = algorithms::make_index(counts);
-
-    return m;
+            [](const mc_segment_ptr& s) { return s->num_compartments(); });
 }
 
-void cell::add_stimulus(segment_location loc, i_clamp stim) {
+void mc_cell::add_stimulus(segment_location loc, i_clamp stim) {
     (void)segment(loc.segment); // assert loc.segment in range
     stimuli_.push_back({loc, std::move(stim)});
 }
 
-void cell::add_detector(segment_location loc, double threshold) {
+void mc_cell::add_detector(segment_location loc, double threshold) {
     spike_detectors_.push_back({loc, threshold});
 }
 
-// Rough and ready comparison of two cells.
-// We don't use an operator== because equality of two cells is open to
-// interpretation. For example, it is possible to have two viable representations
-// of a cell: with and without location information for the cables.
-//
-// Checks that two cells have the same
-//  - number and type of segments
-//  - volume and area properties of each segment
-//  - number of compartments in each segment
-bool cell_basic_equality(const cell& lhs, const cell& rhs) {
-    if (lhs.parents_ != rhs.parents_) {
-        return false;
-    }
-
-    for (cell::index_type i=0; i<lhs.num_segments(); ++i) {
-        // a quick and dirty test
-        auto& l = *lhs.segment(i);
-        auto& r = *rhs.segment(i);
-
-        if (l.kind() != r.kind()) return false;
-        if (l.area() != r.area()) return false;
-        if (l.volume() != r.volume()) return false;
-        if (l.as_cable()) {
-            if (l.as_cable()->num_compartments() != r.as_cable()->num_compartments()) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
 
 // Construct cell from flat morphology specification.
 
-cell make_cell(const morphology& morph, bool compartments_from_discretization) {
-    using point3d = cell::point_type;
-    cell newcell;
+mc_cell make_mc_cell(const morphology& morph, bool compartments_from_discretization) {
+    using point3d = mc_cell::point_type;
+    mc_cell newcell;
 
     if (!morph) {
         return newcell;
@@ -178,7 +132,7 @@ cell make_cell(const morphology& morph, bool compartments_from_discretization) {
         default: ;
         }
 
-        std::vector<cell::value_type> radii;
+        std::vector<value_type> radii;
         std::vector<point3d> points;
 
         for (const section_point& p: section.points) {
diff --git a/arbor/mc_cell_group.cpp b/arbor/mc_cell_group.cpp
index 6988757e..b5e12cc2 100644
--- a/arbor/mc_cell_group.cpp
+++ b/arbor/mc_cell_group.cpp
@@ -4,11 +4,10 @@
 
 #include <arbor/assert.hpp>
 #include <arbor/common_types.hpp>
+#include <arbor/sampling.hpp>
 #include <arbor/spike.hpp>
 
 #include "backends/event.hpp"
-#include "cell.hpp"
-#include "cell_group.hpp"
 #include "cell_group.hpp"
 #include "event_binner.hpp"
 #include "event_queue.hpp"
@@ -17,9 +16,10 @@
 #include "profile/profiler_macro.hpp"
 #include "recipe.hpp"
 #include "sampler_map.hpp"
-#include "sampling.hpp"
 #include "util/filter.hpp"
+#include "util/maputil.hpp"
 #include "util/partition.hpp"
+#include "util/span.hpp"
 
 namespace arb {
 
@@ -30,7 +30,7 @@ mc_cell_group::mc_cell_group(std::vector<cell_gid_type> gids, const recipe& rec,
     set_binning_policy(binning_kind::none, 0);
 
     // Build lookup table for gid to local index.
-    for (auto i: util::make_span(0, gids_.size())) {
+    for (auto i: util::count_along(gids_)) {
         gid_index_map_[gids_[i]] = i;
     }
 
@@ -83,7 +83,7 @@ void mc_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& e
     staged_events_.clear();
     // skip event binning if empty lanes are passed
     if (event_lanes.size()) {
-        for (auto lid: util::make_span(0, gids_.size())) {
+        for (auto lid: util::count_along(gids_)) {
             auto& lane = event_lanes[lid];
             for (auto e: lane) {
                 if (e.time>=ep.tfinal) break;
diff --git a/arbor/mc_cell_group.hpp b/arbor/mc_cell_group.hpp
index 2609858b..463e59d0 100644
--- a/arbor/mc_cell_group.hpp
+++ b/arbor/mc_cell_group.hpp
@@ -8,10 +8,10 @@
 
 #include <arbor/assert.hpp>
 #include <arbor/common_types.hpp>
+#include <arbor/sampling.hpp>
 #include <arbor/spike.hpp>
 
 #include "backends/event.hpp"
-#include "cell.hpp"
 #include "cell_group.hpp"
 #include "event_binner.hpp"
 #include "event_queue.hpp"
@@ -19,12 +19,10 @@
 #include "profile/profiler_macro.hpp"
 #include "recipe.hpp"
 #include "sampler_map.hpp"
-#include "sampling.hpp"
 #include "util/double_buffer.hpp"
 #include "util/filter.hpp"
 #include "util/partition.hpp"
 #include "util/range.hpp"
-#include "util/unique_any.hpp"
 
 namespace arb {
 
diff --git a/arbor/mechcat.cpp b/arbor/mechcat.cpp
index 11231771..5ce86884 100644
--- a/arbor/mechcat.cpp
+++ b/arbor/mechcat.cpp
@@ -4,9 +4,9 @@
 #include <vector>
 
 #include <arbor/mechcat.hpp>
+#include <arbor/util/make_unique.hpp>
 
 #include "util/maputil.hpp"
-#include "util/make_unique.hpp"
 
 namespace arb {
 
diff --git a/arbor/merge_events.cpp b/arbor/merge_events.cpp
index fcd31dc3..4e5d8600 100644
--- a/arbor/merge_events.cpp
+++ b/arbor/merge_events.cpp
@@ -1,15 +1,15 @@
+#include <iostream>
 #include <set>
 #include <vector>
 
-#include <backends.hpp>
-#include <cell_group.hpp>
-#include <cell_group_factory.hpp>
-#include <domain_decomposition.hpp>
-#include <merge_events.hpp>
-#include <recipe.hpp>
-#include <util/filter.hpp>
-#include <util/span.hpp>
-#include <util/unique_any.hpp>
+#include "backends.hpp"
+#include "cell_group.hpp"
+#include "cell_group_factory.hpp"
+#include "domain_decomposition.hpp"
+#include "merge_events.hpp"
+#include "recipe.hpp"
+#include "util/filter.hpp"
+#include "util/span.hpp"
 #include "profile/profiler_macro.hpp"
 
 namespace arb {
@@ -53,12 +53,16 @@ tourney_tree::tourney_tree(std::vector<event_generator>& input):
     setup(0);
 }
 
-void tourney_tree::print() const {
-    auto nxt=1u;
-    for (auto i=0u; i<nodes_; ++i) {
-        if (i==nxt-1) { nxt*=2; std::cout << "\n";}
-        std::cout << "{" << heap_[i].first << "," << heap_[i].second << "}\n";
+std::ostream& operator<<(std::ostream& out, const tourney_tree& tt) {
+    unsigned nxt = 1;
+    for (unsigned i = 0; i<tt.nodes_; ++i) {
+        if (i==nxt-1) {
+            nxt*=2;
+            out << "\n";
+        }
+        out << "{" << tt.heap_[i].first << "," << tt.heap_[i].second << "}\n";
     }
+    return out;
 }
 
 bool tourney_tree::empty() const {
diff --git a/arbor/merge_events.hpp b/arbor/merge_events.hpp
index eb0d2873..72c3918d 100644
--- a/arbor/merge_events.hpp
+++ b/arbor/merge_events.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <algorithm>
+#include <iosfwd>
 #include <vector>
 
 #include <event_generator.hpp>
@@ -56,7 +57,7 @@ namespace impl {
         bool empty(time_type t) const;
         postsynaptic_spike_event head() const;
         void pop();
-        void print() const;
+        friend std::ostream& operator<<(std::ostream&, const tourney_tree&);
 
     private:
         void setup(unsigned i);
diff --git a/arbor/morphology.cpp b/arbor/morphology.cpp
index ea0a4581..5508f835 100644
--- a/arbor/morphology.cpp
+++ b/arbor/morphology.cpp
@@ -1,9 +1,9 @@
 #include <cmath>
 #include <vector>
 
-#include <math.hpp>
+#include <arbor/morphology.hpp>
 
-#include <morphology.hpp>
+#include "math.hpp"
 
 namespace arb {
 
diff --git a/arbor/partition_load_balance.cpp b/arbor/partition_load_balance.cpp
index 9cff1bd2..062373de 100644
--- a/arbor/partition_load_balance.cpp
+++ b/arbor/partition_load_balance.cpp
@@ -4,6 +4,7 @@
 #include "domain_decomposition.hpp"
 #include "hardware/node_info.hpp"
 #include "recipe.hpp"
+#include "util/span.hpp"
 
 namespace arb {
 
@@ -40,7 +41,7 @@ domain_decomposition partition_load_balance(const recipe& rec,
 
     std::vector<cell_gid_type> gid_divisions;
     auto gid_part = make_partition(
-        gid_divisions, transform_view(make_span(0, num_domains), dom_size));
+        gid_divisions, transform_view(make_span(num_domains), dom_size));
 
     // Local load balance
 
diff --git a/arbor/recipe.hpp b/arbor/recipe.hpp
index 1ce9453d..8d163c35 100644
--- a/arbor/recipe.hpp
+++ b/arbor/recipe.hpp
@@ -6,10 +6,9 @@
 #include <stdexcept>
 
 #include <arbor/common_types.hpp>
+#include <arbor/util/unique_any.hpp>
 
-#include "cell.hpp"
 #include "event_generator.hpp"
-#include "util/unique_any.hpp"
 
 namespace arb {
 
diff --git a/arbor/sampler_map.hpp b/arbor/sampler_map.hpp
index cd6a1576..08040015 100644
--- a/arbor/sampler_map.hpp
+++ b/arbor/sampler_map.hpp
@@ -10,8 +10,8 @@
 #include <unordered_map>
 
 #include <arbor/common_types.hpp>
+#include <arbor/sampling.hpp>
 
-#include "sampling.hpp"
 #include "schedule.hpp"
 #include "util/deduce_return.hpp"
 #include "util/transform.hpp"
diff --git a/arbor/simulation.cpp b/arbor/simulation.cpp
index 0fbde656..5134e714 100644
--- a/arbor/simulation.cpp
+++ b/arbor/simulation.cpp
@@ -2,18 +2,17 @@
 #include <set>
 #include <vector>
 
-#include <backends.hpp>
-#include <cell_group.hpp>
-#include <cell_group_factory.hpp>
-#include <domain_decomposition.hpp>
-#include <merge_events.hpp>
-#include <simulation.hpp>
-#include <recipe.hpp>
-#include <thread_private_spike_store.hpp>
-#include <util/double_buffer.hpp>
-#include <util/filter.hpp>
-#include <util/span.hpp>
-#include <util/unique_any.hpp>
+#include "backends.hpp"
+#include "cell_group.hpp"
+#include "cell_group_factory.hpp"
+#include "domain_decomposition.hpp"
+#include "merge_events.hpp"
+#include "simulation.hpp"
+#include "recipe.hpp"
+#include "thread_private_spike_store.hpp"
+#include "util/double_buffer.hpp"
+#include "util/filter.hpp"
+#include "util/span.hpp"
 #include "profile/profiler_macro.hpp"
 
 namespace arb {
diff --git a/arbor/simulation.hpp b/arbor/simulation.hpp
index 9c178742..d5be946e 100644
--- a/arbor/simulation.hpp
+++ b/arbor/simulation.hpp
@@ -7,6 +7,7 @@
 
 #include <arbor/common_types.hpp>
 #include <arbor/distributed_context.hpp>
+#include <arbor/sampling.hpp>
 
 #include "backends.hpp"
 #include "cell_group.hpp"
@@ -14,7 +15,6 @@
 #include "domain_decomposition.hpp"
 #include "epoch.hpp"
 #include "recipe.hpp"
-#include "sampling.hpp"
 #include "util/nop.hpp"
 #include "util/handle_set.hpp"
 
diff --git a/arbor/spike_source_cell_group.cpp b/arbor/spike_source_cell_group.cpp
index 0bef8303..d62ac2c2 100644
--- a/arbor/spike_source_cell_group.cpp
+++ b/arbor/spike_source_cell_group.cpp
@@ -1,11 +1,13 @@
 #include <exception>
 
-#include <cell_group.hpp>
+#include <arbor/spike_source_cell.hpp>
+#include <arbor/time_sequence.hpp>
+
+#include "cell_group.hpp"
 #include "profile/profiler_macro.hpp"
-#include <recipe.hpp>
-#include <spike_source_cell.hpp>
-#include <spike_source_cell_group.hpp>
-#include <time_sequence.hpp>
+#include "recipe.hpp"
+#include "spike_source_cell_group.hpp"
+#include "util/span.hpp"
 
 namespace arb {
 
@@ -31,7 +33,7 @@ cell_kind spike_source_cell_group::get_cell_kind() const {
 void spike_source_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& event_lanes) {
     PE(advance_sscell);
 
-    for (auto i: util::make_span(0, gids_.size())) {
+    for (auto i: util::count_along(gids_)) {
         auto& tseq = time_sequences_[i];
         const auto gid = gids_[i];
 
diff --git a/arbor/spike_source_cell_group.hpp b/arbor/spike_source_cell_group.hpp
index d4d52497..da714570 100644
--- a/arbor/spike_source_cell_group.hpp
+++ b/arbor/spike_source_cell_group.hpp
@@ -1,8 +1,9 @@
 #pragma once
 
-#include <cell_group.hpp>
-#include <recipe.hpp>
-#include <time_sequence.hpp>
+#include <arbor/time_sequence.hpp>
+
+#include "cell_group.hpp"
+#include "recipe.hpp"
 
 namespace arb {
 
diff --git a/arbor/swcio.cpp b/arbor/swcio.cpp
index 02f49f0d..2f93bef8 100644
--- a/arbor/swcio.cpp
+++ b/arbor/swcio.cpp
@@ -6,12 +6,11 @@
 #include <unordered_set>
 
 #include <arbor/assert.hpp>
+#include <arbor/morphology.hpp>
+#include <arbor/point.hpp>
 
-#include <algorithms.hpp>
-#include <cell.hpp>
-#include <morphology.hpp>
-#include <point.hpp>
-#include <swcio.hpp>
+#include "algorithms.hpp"
+#include "swcio.hpp"
 
 namespace arb {
 namespace io {
diff --git a/arbor/swcio.hpp b/arbor/swcio.hpp
index 84996293..15028118 100644
--- a/arbor/swcio.hpp
+++ b/arbor/swcio.hpp
@@ -9,10 +9,10 @@
 #include <vector>
 
 #include <arbor/assert.hpp>
+#include <arbor/morphology.hpp>
+#include <arbor/point.hpp>
 
-#include <algorithms.hpp>
-#include <morphology.hpp>
-#include <point.hpp>
+#include "algorithms.hpp"
 
 namespace arb {
 namespace io {
diff --git a/example/bench/recipe.cpp b/example/bench/recipe.cpp
index 50787c08..e70e8060 100644
--- a/example/bench/recipe.cpp
+++ b/example/bench/recipe.cpp
@@ -1,9 +1,8 @@
 #include <random>
 
+#include <arbor/benchmark_cell.hpp>
 #include <arbor/common_types.hpp>
-
-#include <benchmark_cell.hpp>
-#include <time_sequence.hpp>
+#include <arbor/time_sequence.hpp>
 
 #include "recipe.hpp"
 
diff --git a/example/brunel/brunel_miniapp.cpp b/example/brunel/brunel_miniapp.cpp
index 6ddbebeb..bb2af814 100644
--- a/example/brunel/brunel_miniapp.cpp
+++ b/example/brunel/brunel_miniapp.cpp
@@ -8,9 +8,11 @@
 
 #include <arbor/common_types.hpp>
 #include <arbor/distributed_context.hpp>
+#include <arbor/lif_cell.hpp>
 #include <arbor/profile/meter_manager.hpp>
 #include <arbor/profile/profiler.hpp>
 #include <arbor/threadinfo.hpp>
+#include <arbor/util/make_unique.hpp>
 #include <arbor/version.hpp>
 
 #include "json_meter.hpp"
@@ -18,12 +20,10 @@
 #include "with_mpi.hpp"
 #endif
 
-#include "communication/communicator.hpp"
 #include "event_generator.hpp"
 #include "hardware/gpu.hpp"
 #include "hardware/node_info.hpp"
 #include "io/exporter_spike_file.hpp"
-#include "lif_cell_description.hpp"
 #include "recipe.hpp"
 #include "simulation.hpp"
 #include "util/ioutil.hpp"
@@ -110,7 +110,7 @@ public:
     }
 
     util::unique_any get_cell_description(cell_gid_type gid) const override {
-        auto cell = lif_cell_description();
+        auto cell = lif_cell();
         cell.tau_m = 10;
         cell.V_th = 10;
         cell.C_m = 20;
diff --git a/example/generators/event_gen.cpp b/example/generators/event_gen.cpp
index 3deb5ba0..5a21764d 100644
--- a/example/generators/event_gen.cpp
+++ b/example/generators/event_gen.cpp
@@ -14,14 +14,14 @@
 
 #include <arbor/common_types.hpp>
 #include <arbor/distributed_context.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/simple_sampler.hpp>
 
-#include "cell.hpp"
 #include "event_generator.hpp"
 #include "hardware/node_info.hpp"
 #include "load_balance.hpp"
 #include "simulation.hpp"
 #include "recipe.hpp"
-#include "simple_sampler.hpp"
 
 using arb::cell_gid_type;
 using arb::cell_lid_type;
@@ -48,7 +48,7 @@ public:
     //    capacitance: 0.01 F/m² [default]
     //    synapses: 1 * expsyn
     arb::util::unique_any get_cell_description(cell_gid_type gid) const override {
-        arb::cell c;
+        arb::mc_cell c;
 
         c.add_soma(18.8/2.0); // convert 18.8 μm diameter to radius
         c.soma()->add_mechanism("pas");
diff --git a/example/miniapp/miniapp.cpp b/example/miniapp/miniapp.cpp
index 9d90872d..6f43f54a 100644
--- a/example/miniapp/miniapp.cpp
+++ b/example/miniapp/miniapp.cpp
@@ -7,21 +7,21 @@
 
 #include <arbor/common_types.hpp>
 #include <arbor/distributed_context.hpp>
+#include <arbor/mc_cell.hpp>
 #include <arbor/profile/meter_manager.hpp>
 #include <arbor/profile/profiler.hpp>
+#include <arbor/sampling.hpp>
 #include <arbor/threadinfo.hpp>
+#include <arbor/util/any.hpp>
 #include <arbor/version.hpp>
 
 #include "communication/communicator.hpp"
-#include "cell.hpp"
 #include "hardware/gpu.hpp"
 #include "hardware/node_info.hpp"
 #include "io/exporter_spike_file.hpp"
 #include "load_balance.hpp"
 #include "simulation.hpp"
-#include "sampling.hpp"
 #include "schedule.hpp"
-#include "util/any.hpp"
 #include "util/ioutil.hpp"
 
 #include "json_meter.hpp"
@@ -250,7 +250,7 @@ void report_compartment_stats(const recipe& rec) {
     for (std::size_t i = 0; i<ncell; ++i) {
         std::size_t ncomp = 0;
         auto c = rec.get_cell_description(i);
-        if (auto ptr = any_cast<cell>(&c)) {
+        if (auto ptr = any_cast<mc_cell>(&c)) {
             ncomp = ptr->num_compartments();
         }
         ncomp_total += ncomp;
diff --git a/example/miniapp/miniapp_recipes.cpp b/example/miniapp/miniapp_recipes.cpp
index b8ab4e14..dc250abc 100644
--- a/example/miniapp/miniapp_recipes.cpp
+++ b/example/miniapp/miniapp_recipes.cpp
@@ -4,12 +4,12 @@
 #include <utility>
 
 #include <arbor/assert.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/morphology.hpp>
+#include <arbor/spike_source_cell.hpp>
+#include <arbor/time_sequence.hpp>
 
-#include "cell.hpp"
 #include "event_generator.hpp"
-#include "morphology.hpp"
-#include "spike_source_cell.hpp"
-#include "time_sequence.hpp"
 
 #include "io.hpp"
 #include "miniapp_recipes.hpp"
@@ -21,14 +21,14 @@ namespace arb {
 // description for greater data reuse.
 
 template <typename RNG>
-cell make_basic_cell(
+mc_cell make_basic_cell(
     const morphology& morph,
     unsigned compartments_per_segment,
     unsigned num_synapses,
     const std::string& syn_type,
     RNG& rng)
 {
-    arb::cell cell = make_cell(morph, true);
+    mc_cell cell = make_mc_cell(morph, true);
 
     for (auto& segment: cell.segments()) {
         if (compartments_per_segment!=0) {
diff --git a/example/miniapp/morphology_pool.cpp b/example/miniapp/morphology_pool.cpp
index aeda5436..4d3937e4 100644
--- a/example/miniapp/morphology_pool.cpp
+++ b/example/miniapp/morphology_pool.cpp
@@ -2,9 +2,10 @@
 #include <memory>
 #include <vector>
 
-#include <morphology.hpp>
-#include <swcio.hpp>
-#include <util/path.hpp>
+#include <arbor/morphology.hpp>
+
+#include "swcio.hpp"
+#include "util/path.hpp"
 
 #include "morphology_pool.hpp"
 
diff --git a/example/miniapp/morphology_pool.hpp b/example/miniapp/morphology_pool.hpp
index 34fb01c8..a7b46230 100644
--- a/example/miniapp/morphology_pool.hpp
+++ b/example/miniapp/morphology_pool.hpp
@@ -8,8 +8,9 @@
 #include <string>
 #include <vector>
 
-#include <morphology.hpp>
-#include <util/path.hpp>
+#include <arbor/morphology.hpp>
+
+#include "util/path.hpp"
 
 namespace arb {
 
diff --git a/example/miniapp/trace.hpp b/example/miniapp/trace.hpp
index 23db694a..4cf2a29c 100644
--- a/example/miniapp/trace.hpp
+++ b/example/miniapp/trace.hpp
@@ -8,8 +8,7 @@
 #include <vector>
 
 #include <arbor/common_types.hpp>
-
-#include "simple_sampler.hpp"
+#include <arbor/simple_sampler.hpp>
 
 struct sample_trace {
     arb::cell_member_type probe_id;
diff --git a/arbor/benchmark_cell.hpp b/include/arbor/benchmark_cell.hpp
similarity index 93%
rename from arbor/benchmark_cell.hpp
rename to include/arbor/benchmark_cell.hpp
index 6d3c928c..cbb32ba1 100644
--- a/arbor/benchmark_cell.hpp
+++ b/include/arbor/benchmark_cell.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <time_sequence.hpp>
+#include <arbor/time_sequence.hpp>
 
 namespace arb {
 
diff --git a/arbor/constants.hpp b/include/arbor/constants.hpp
similarity index 100%
rename from arbor/constants.hpp
rename to include/arbor/constants.hpp
diff --git a/arbor/lif_cell_description.hpp b/include/arbor/lif_cell.hpp
similarity index 94%
rename from arbor/lif_cell_description.hpp
rename to include/arbor/lif_cell.hpp
index db5f4d50..e2af157a 100644
--- a/arbor/lif_cell_description.hpp
+++ b/include/arbor/lif_cell.hpp
@@ -3,7 +3,7 @@
 namespace arb {
 
 // Model parameteres of leaky integrate and fire neuron model.
-struct lif_cell_description {
+struct lif_cell {
     // Neuronal parameters.
     double tau_m = 10;    // Membrane potential decaying constant [ms].
     double V_th = 10;     // Firing threshold [mV].
diff --git a/arbor/cell.hpp b/include/arbor/mc_cell.hpp
similarity index 85%
rename from arbor/cell.hpp
rename to include/arbor/mc_cell.hpp
index 0354fd40..b9e4b101 100644
--- a/arbor/cell.hpp
+++ b/include/arbor/mc_cell.hpp
@@ -5,14 +5,12 @@
 #include <vector>
 
 #include <arbor/common_types.hpp>
-#include <arbor/mechcat.hpp>
+#include <arbor/constants.hpp>
 #include <arbor/ion.hpp>
+#include <arbor/mechcat.hpp>
+#include <arbor/morphology.hpp>
+#include <arbor/mc_segment.hpp>
 
-#include "constants.hpp"
-#include "morphology.hpp"
-#include "segment.hpp"
-#include "tree.hpp"
-#include "util/rangeutil.hpp"
 
 namespace arb {
 
@@ -60,7 +58,7 @@ struct cell_probe_address {
 
 // Global parameter type for cell descriptions.
 
-struct cell_global_properties {
+struct mc_cell_global_properties {
     const mechanism_catalogue* catalogue = &global_default_catalogue();
 
     // If >0, check membrane voltage magnitude is less than limit
@@ -86,17 +84,8 @@ struct cell_global_properties {
     double init_membrane_potential_mV = -65; // [mV]
 };
 
-/// Wrapper around compartment layout information derived from a high level cell
-/// description.
-
-struct compartment_model {
-    arb::tree tree;
-    std::vector<tree::int_type> parent_index;
-    std::vector<tree::int_type> segment_index;
-};
-
 /// high-level abstract representation of a cell and its segments
-class cell {
+class mc_cell {
 public:
     using index_type = cell_lid_type;
     using size_type = cell_local_size_type;
@@ -119,10 +108,10 @@ public:
     };
 
     /// Default constructor
-    cell();
+    mc_cell();
 
     /// Copy constructor
-    cell(const cell& other):
+    mc_cell(const mc_cell& other):
         parents_(other.parents_),
         stimuli_(other.stimuli_),
         synapses_(other.synapses_),
@@ -136,7 +125,7 @@ public:
     }
 
     /// Move constructor
-    cell(cell&& other) = default;
+    mc_cell(mc_cell&& other) = default;
 
     /// Return the kind of cell, used for grouping into cell_groups
     cell_kind get_cell_kind() const  {
@@ -150,7 +139,7 @@ public:
     /// add a cable
     /// parent is the index of the parent segment for the cable section
     /// cable is the segment that will be moved into the cell
-    cable_segment* add_cable(index_type parent, segment_ptr&& cable);
+    cable_segment* add_cable(index_type parent, mc_segment_ptr&& cable);
 
     /// add a cable by constructing it in place
     /// parent is the index of the parent segment for the cable section
@@ -163,8 +152,8 @@ public:
 
     bool has_soma() const;
 
-    class segment* segment(index_type index);
-    const class segment* segment(index_type index) const;
+    class mc_segment* segment(index_type index);
+    const class mc_segment* segment(index_type index) const;
 
     /// access pointer to the soma
     /// returns nullptr if the cell has no soma
@@ -176,24 +165,16 @@ public:
     /// the cable index is not valid
     cable_segment* cable(index_type index);
 
-    /// the volume of the cell
-    value_type volume() const;
-
-    /// the surface area of the cell
-    value_type area() const;
-
     /// the total number of compartments over all segments
     size_type num_compartments() const;
 
-    std::vector<segment_ptr> const& segments() const {
+    std::vector<mc_segment_ptr> const& segments() const {
         return segments_;
     }
 
     /// return a vector with the compartment count for each segment in the cell
     std::vector<size_type> compartment_counts() const;
 
-    compartment_model model() const;
-
     //////////////////
     // stimuli
     //////////////////
@@ -240,7 +221,12 @@ public:
     //  - volume and area properties of each segment
     //  - number of compartments in each segment
     // (note: just used for testing: move to test code?)
-    friend bool cell_basic_equality(const cell&, const cell&);
+    friend bool cell_basic_equality(const mc_cell&, const mc_cell&);
+
+    // Public view of parent indices vector.
+    const std::vector<index_type>& parents() const {
+        return parents_;
+    }
 
 private:
     void assert_valid_segment(index_type) const;
@@ -249,7 +235,7 @@ private:
     std::vector<index_type> parents_;
 
     // the segments
-    std::vector<segment_ptr> segments_;
+    std::vector<mc_segment_ptr> segments_;
 
     // the stimuli
     std::vector<stimulus_instance> stimuli_;
@@ -263,7 +249,7 @@ private:
 
 // create a cable by forwarding cable construction parameters provided by the user
 template <typename... Args>
-cable_segment* cell::add_cable(cell::index_type parent, Args&&... args)
+cable_segment* mc_cell::add_cable(mc_cell::index_type parent, Args&&... args)
 {
     // check for a valid parent id
     if(parent>=num_segments()) {
@@ -281,6 +267,6 @@ cable_segment* cell::add_cable(cell::index_type parent, Args&&... args)
 // If compartments_from_discretization is true, set number of compartments in
 // each segment to be the number of piecewise linear sections in the corresponding
 // section of the morphologu.
-cell make_cell(const morphology&, bool compartments_from_discretization=false);
+mc_cell make_mc_cell(const morphology&, bool compartments_from_discretization=false);
 
 } // namespace arb
diff --git a/arbor/segment.hpp b/include/arbor/mc_segment.hpp
similarity index 74%
rename from arbor/segment.hpp
rename to include/arbor/mc_segment.hpp
index 32a2a7f5..c28ec316 100644
--- a/arbor/segment.hpp
+++ b/include/arbor/mc_segment.hpp
@@ -1,21 +1,21 @@
 #pragma once
 
+#include <algorithm>
 #include <cmath>
+#include <memory>
+#include <numeric>
 #include <stdexcept>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
+#include <arbor/assert.hpp>
 #include <arbor/common_types.hpp>
+#include <arbor/morphology.hpp>
 #include <arbor/mechinfo.hpp>
-
-#include "algorithms.hpp"
-#include "compartment.hpp"
-#include "math.hpp"
-#include "morphology.hpp"
-#include "point.hpp"
-#include "util/make_unique.hpp"
-#include "util/maputil.hpp"
+#include <arbor/point.hpp>
+#include <arbor/util/make_unique.hpp>
+#include <arbor/util/optional.hpp>
 
 namespace arb {
 
@@ -54,8 +54,11 @@ struct mechanism_desc {
     }
 
     double get(const std::string& key) const {
-        auto optv = util::value_by_key(param_, key);
-        return optv? *optv: throw std::out_of_range("no field "+key+" set");
+        auto i = param_.find(key);
+        if (i==param_.end()) {
+            throw std::out_of_range("no field "+key+" set");
+        }
+        return i->second;
     }
 
     const std::unordered_map<std::string, double>& values() const {
@@ -74,14 +77,14 @@ class soma_segment;
 class cable_segment;
 
 // abstract base class for a cell segment
-class segment {
+class mc_segment {
 public:
     using value_type = double;
     using size_type = cell_local_size_type;
     using point_type = point<value_type>;
 
     // (Yet more motivation for a separate morphology description class!)
-    virtual std::unique_ptr<segment> clone() const = 0;
+    virtual std::unique_ptr<mc_segment> clone() const = 0;
 
     section_kind kind() const {
         return kind_;
@@ -105,10 +108,7 @@ public:
     virtual size_type num_compartments() const = 0;
     virtual void set_compartments(size_type) = 0;
 
-    virtual value_type volume() const = 0;
-    virtual value_type area()   const = 0;
-
-    virtual ~segment() = default;
+    virtual ~mc_segment() = default;
 
     virtual cable_segment* as_cable()
     {
@@ -164,31 +164,21 @@ public:
     value_type cm = 0.01;    // capacitance [F/m^2] : 10 nF/mm^2 = 0.01 F/m^2
 
 protected:
-    segment(section_kind kind): kind_(kind) {}
+    mc_segment(section_kind kind): kind_(kind) {}
 
     section_kind kind_;
     std::vector<mechanism_desc> mechanisms_;
 };
 
-class placeholder_segment : public segment {
+class placeholder_segment : public mc_segment {
 public:
-    placeholder_segment(): segment(section_kind::none) {}
+    placeholder_segment(): mc_segment(section_kind::none) {}
 
-    std::unique_ptr<segment> clone() const override {
+    std::unique_ptr<mc_segment> clone() const override {
         // use default copy constructor
         return util::make_unique<placeholder_segment>(*this);
     }
 
-    value_type volume() const override
-    {
-        return std::numeric_limits<value_type>::quiet_NaN();
-    }
-
-    value_type area() const override
-    {
-        return std::numeric_limits<value_type>::quiet_NaN();
-    }
-
     bool is_placeholder() const override
     {
         return true;
@@ -202,28 +192,18 @@ public:
     virtual void set_compartments(size_type) override {}
 };
 
-class soma_segment : public segment {
+class soma_segment : public mc_segment {
 public:
     soma_segment() = delete;
 
     explicit soma_segment(value_type r, point_type c = point_type{}):
-        segment(section_kind::soma), radius_{r}, center_(c) {}
+       mc_segment(section_kind::soma), radius_{r}, center_(c) {}
 
-    std::unique_ptr<segment> clone() const override {
+    std::unique_ptr<mc_segment> clone() const override {
         // use default copy constructor
         return util::make_unique<soma_segment>(*this);
     }
 
-    value_type volume() const override
-    {
-        return math::volume_sphere(radius_);
-    }
-
-    value_type area() const override
-    {
-        return math::area_sphere(radius_);
-    }
-
     value_type radius() const
     {
         return radius_;
@@ -259,9 +239,9 @@ private :
     point_type center_;
 };
 
-class cable_segment : public segment {
+class cable_segment : public mc_segment {
 public:
-    using base = segment;
+    using base = mc_segment;
     using base::kind_;
     using base::value_type;
     using base::point_type;
@@ -271,7 +251,7 @@ public:
 
     // constructors for a cable with no location information
     cable_segment(section_kind k, std::vector<value_type> r, std::vector<value_type> lens):
-        segment(k), radii_(std::move(r)), lengths_(std::move(lens))
+        mc_segment(k), radii_(std::move(r)), lengths_(std::move(lens))
     {
         arb_assert(kind_==section_kind::dendrite || kind_==section_kind::axon);
     }
@@ -283,7 +263,7 @@ public:
     // constructor that lets the user describe the cable as a
     // seriew of radii and locations
     cable_segment(section_kind k, std::vector<value_type> r, std::vector<point_type> p):
-        segment(k), radii_(std::move(r)), locations_(std::move(p))
+        mc_segment(k), radii_(std::move(r)), locations_(std::move(p))
     {
         arb_assert(kind_==section_kind::dendrite || kind_==section_kind::axon);
         update_lengths();
@@ -302,32 +282,14 @@ public:
         cable_segment(k, {r1, r2}, {p1, p2})
     {}
 
-    std::unique_ptr<segment> clone() const override {
+    std::unique_ptr<mc_segment> clone() const override {
         // use default copy constructor
         return util::make_unique<cable_segment>(*this);
     }
 
-    value_type volume() const override
-    {
-        auto sum = value_type{0};
-        for (auto i=0u; i<num_sub_segments(); ++i) {
-            sum += math::volume_frustrum(lengths_[i], radii_[i], radii_[i+1]);
-        }
-        return sum;
-    }
-
-    value_type area() const override
-    {
-        auto sum = value_type{0};
-        for (auto i=0u; i<num_sub_segments(); ++i) {
-            sum += math::area_frustrum(lengths_[i], radii_[i], radii_[i+1]);
-        }
-        return sum;
-    }
-
     value_type length() const
     {
-        return algorithms::sum(lengths_);
+        return std::accumulate(lengths_.begin(), lengths_.end(), value_type{});
     }
 
     bool has_locations() const
@@ -402,12 +364,6 @@ public:
         return rel*radii_[i] + (1.-rel)*radii_[i+1];
     }
 
-    /// iterable range type for simple compartment representation
-    compartment_range<size_type, value_type> compartments() const
-    {
-        return make_compartment_range(num_compartments(), radii_.front(), radii_.back(), length());
-    }
-
 private:
     void update_lengths() {
         if (locations_.size()) {
@@ -424,27 +380,14 @@ private:
     std::vector<point_type> locations_;
 };
 
-/// Unique pointer wrapper for abstract segment base class
-using segment_ptr = std::unique_ptr<segment>;
+using mc_segment_ptr = std::unique_ptr<mc_segment>;
 
 /// Helper for constructing segments in a segment_ptr unique pointer wrapper.
 /// Forwards the supplied arguments to construct a segment of type SegmentType.
 /// e.g. auto my_cable = make_segment<cable>(section_kind::dendrite, ... );
 template <typename SegmentType, typename... Args>
-segment_ptr make_segment(Args&&... args) {
-    return segment_ptr(new SegmentType(std::forward<Args>(args)...));
-}
-
-/// Divided compartment adaptors for cable segments
-
-template <typename DivCompClass>
-DivCompClass div_compartments(const cable_segment* cable, unsigned ncomp) {
-    return DivCompClass(ncomp, cable->radii(), cable->lengths());
-}
-
-template <typename DivCompClass>
-DivCompClass div_compartments(const cable_segment* cable) {
-    return DivCompClass(cable->num_compartments(), cable->radii(), cable->lengths());
+mc_segment_ptr make_segment(Args&&... args) {
+    return mc_segment_ptr(new SegmentType(std::forward<Args>(args)...));
 }
 
 } // namespace arb
diff --git a/arbor/morphology.hpp b/include/arbor/morphology.hpp
similarity index 100%
rename from arbor/morphology.hpp
rename to include/arbor/morphology.hpp
diff --git a/arbor/point.hpp b/include/arbor/point.hpp
similarity index 100%
rename from arbor/point.hpp
rename to include/arbor/point.hpp
diff --git a/arbor/sampling.hpp b/include/arbor/sampling.hpp
similarity index 95%
rename from arbor/sampling.hpp
rename to include/arbor/sampling.hpp
index 04337633..97a89cd4 100644
--- a/arbor/sampling.hpp
+++ b/include/arbor/sampling.hpp
@@ -4,8 +4,7 @@
 #include <functional>
 
 #include <arbor/common_types.hpp>
-
-#include "util/any_ptr.hpp"
+#include <arbor/util/any_ptr.hpp>
 
 namespace arb {
 
diff --git a/arbor/simd/approx.hpp b/include/arbor/simd/approx.hpp
similarity index 100%
rename from arbor/simd/approx.hpp
rename to include/arbor/simd/approx.hpp
diff --git a/arbor/simd/avx.hpp b/include/arbor/simd/avx.hpp
similarity index 99%
rename from arbor/simd/avx.hpp
rename to include/arbor/simd/avx.hpp
index ae3dd27e..78a566f0 100644
--- a/arbor/simd/avx.hpp
+++ b/include/arbor/simd/avx.hpp
@@ -8,8 +8,8 @@
 #include <cstdint>
 #include <immintrin.h>
 
-#include <simd/approx.hpp>
-#include <simd/implbase.hpp>
+#include <arbor/simd/approx.hpp>
+#include <arbor/simd/implbase.hpp>
 
 namespace arb {
 namespace simd {
diff --git a/arbor/simd/avx512.hpp b/include/arbor/simd/avx512.hpp
similarity index 99%
rename from arbor/simd/avx512.hpp
rename to include/arbor/simd/avx512.hpp
index 6f123612..dd723c3f 100644
--- a/arbor/simd/avx512.hpp
+++ b/include/arbor/simd/avx512.hpp
@@ -8,8 +8,8 @@
 #include <cstdint>
 #include <immintrin.h>
 
-#include <simd/approx.hpp>
-#include <simd/implbase.hpp>
+#include <arbor/simd/approx.hpp>
+#include <arbor/simd/implbase.hpp>
 
 namespace arb {
 namespace simd {
diff --git a/arbor/simd/generic.hpp b/include/arbor/simd/generic.hpp
similarity index 97%
rename from arbor/simd/generic.hpp
rename to include/arbor/simd/generic.hpp
index e8ca5524..fecdb37f 100644
--- a/arbor/simd/generic.hpp
+++ b/include/arbor/simd/generic.hpp
@@ -4,7 +4,7 @@
 #include <cstring>
 #include <cmath>
 
-#include <simd/implbase.hpp>
+#include <arbor/simd/implbase.hpp>
 
 namespace arb {
 namespace simd {
diff --git a/arbor/simd/implbase.hpp b/include/arbor/simd/implbase.hpp
similarity index 100%
rename from arbor/simd/implbase.hpp
rename to include/arbor/simd/implbase.hpp
diff --git a/arbor/simd/native.hpp b/include/arbor/simd/native.hpp
similarity index 100%
rename from arbor/simd/native.hpp
rename to include/arbor/simd/native.hpp
diff --git a/arbor/simd/simd.hpp b/include/arbor/simd/simd.hpp
similarity index 99%
rename from arbor/simd/simd.hpp
rename to include/arbor/simd/simd.hpp
index b863a283..2bb0c0bb 100644
--- a/arbor/simd/simd.hpp
+++ b/include/arbor/simd/simd.hpp
@@ -4,9 +4,9 @@
 #include <cstddef>
 #include <type_traits>
 
-#include <simd/implbase.hpp>
-#include <simd/generic.hpp>
-#include <simd/native.hpp>
+#include <arbor/simd/implbase.hpp>
+#include <arbor/simd/generic.hpp>
+#include <arbor/simd/native.hpp>
 
 namespace arb {
 namespace simd {
diff --git a/arbor/simd/simd_io.hpp b/include/arbor/simd/simd_io.hpp
similarity index 94%
rename from arbor/simd/simd_io.hpp
rename to include/arbor/simd/simd_io.hpp
index 3cc4c841..96ead475 100644
--- a/arbor/simd/simd_io.hpp
+++ b/include/arbor/simd/simd_io.hpp
@@ -4,7 +4,7 @@
 
 #include <iostream>
 
-#include <simd/simd.hpp>
+#include <arbor/simd/simd.hpp>
 
 namespace arb {
 namespace simd {
diff --git a/arbor/simple_sampler.hpp b/include/arbor/simple_sampler.hpp
similarity index 53%
rename from arbor/simple_sampler.hpp
rename to include/arbor/simple_sampler.hpp
index 8d85ab80..480f59cd 100644
--- a/arbor/simple_sampler.hpp
+++ b/include/arbor/simple_sampler.hpp
@@ -5,15 +5,13 @@
  * trace data from a cell probe, with some metadata.
  */
 
+#include <stdexcept>
+#include <type_traits>
 #include <vector>
 
 #include <arbor/common_types.hpp>
-
-#include "sampling.hpp"
-#include "util/any_ptr.hpp"
-#include "util/deduce_return.hpp"
-#include "util/span.hpp"
-#include "util/transform.hpp"
+#include <arbor/sampling.hpp>
+#include <arbor/util/any_ptr.hpp>
 
 namespace arb {
 
@@ -26,34 +24,13 @@ struct trace_entry {
 template <typename V>
 using trace_data = std::vector<trace_entry<V>>;
 
-// NB: work-around for lack of function return type deduction
-// in C++11; can't use lambda within DEDUCED_RETURN_TYPE.
-
-namespace impl {
-    template <typename V>
-    inline float time(const trace_entry<V>& x) { return x.t; }
-
-    template <typename V>
-    inline const V& value(const trace_entry<V>& x) { return x.v; }
-}
-
-template <typename V>
-inline auto times(const trace_data<V>& trace) DEDUCED_RETURN_TYPE(
-   util::transform_view(trace, impl::time<V>)
-)
-
-template <typename V>
-inline auto values(const trace_data<V>& trace) DEDUCED_RETURN_TYPE(
-   util::transform_view(trace, impl::value<V>)
-)
-
-template <typename V, typename = util::enable_if_trivially_copyable_t<V>>
+template <typename V, typename = typename std::enable_if<std::is_trivially_copyable<V>::value>::type>
 class simple_sampler {
 public:
     explicit simple_sampler(trace_data<V>& trace): trace_(trace) {}
 
     void operator()(cell_member_type probe_id, probe_tag tag, std::size_t n, const sample_record* recs) {
-        for (auto i: util::make_span(0, n)) {
+        for (std::size_t i = 0; i<n; ++i) {
             if (auto p = util::any_cast<const V*>(recs[i].data)) {
                 trace_.push_back({recs[i].time, *p});
             }
diff --git a/arbor/spike_source_cell.hpp b/include/arbor/spike_source_cell.hpp
similarity index 87%
rename from arbor/spike_source_cell.hpp
rename to include/arbor/spike_source_cell.hpp
index 489d1300..74b66e5b 100644
--- a/arbor/spike_source_cell.hpp
+++ b/include/arbor/spike_source_cell.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <time_sequence.hpp>
+#include <arbor/time_sequence.hpp>
 
 namespace arb {
 
diff --git a/arbor/time_sequence.hpp b/include/arbor/time_sequence.hpp
similarity index 96%
rename from arbor/time_sequence.hpp
rename to include/arbor/time_sequence.hpp
index d9845599..5b0fb9ef 100644
--- a/arbor/time_sequence.hpp
+++ b/include/arbor/time_sequence.hpp
@@ -3,11 +3,10 @@
 #include <algorithm>
 #include <memory>
 #include <random>
+#include <type_traits>
 
 #include <arbor/common_types.hpp>
 
-#include "event_queue.hpp"
-#include "util/meta.hpp"
 #include "util/rangeutil.hpp"
 
 namespace arb {
@@ -33,9 +32,10 @@ public:
 
     template <
         typename Impl,
-        typename = typename util::enable_if_t<
+        typename = typename std::enable_if<
             !std::is_same<typename std::decay<Impl>::type,
-                          time_seq>::value>>
+                          time_seq>::value>::type
+    >
     time_seq(Impl&& impl):
         impl_(new wrap<Impl>(std::forward<Impl>(impl)))
     {}
@@ -123,8 +123,8 @@ struct vector_time_seq {
         seq_(std::move(seq))
     {
         // Ensure that the time values are sorted.
-        if(!std::is_sorted(seq_.begin(), seq_.end())) {
-            util::sort(seq_);
+        if (!std::is_sorted(seq_.begin(), seq_.end())) {
+            std::sort(seq_.begin(), seq_.end());
         }
         reset();
     }
diff --git a/arbor/util/any.hpp b/include/arbor/util/any.hpp
similarity index 94%
rename from arbor/util/any.hpp
rename to include/arbor/util/any.hpp
index 1bc1e587..1854f06d 100644
--- a/arbor/util/any.hpp
+++ b/include/arbor/util/any.hpp
@@ -4,8 +4,6 @@
 #include <typeinfo>
 #include <type_traits>
 
-#include <util/meta.hpp>
-
 // Partial implementation of std::any from C++17 standard.
 //      http://en.cppreference.com/w/cpp/utility/any
 //
@@ -40,10 +38,10 @@ public:
 
     template <
         typename T,
-        typename = typename util::enable_if_t<!std::is_same<util::decay_t<T>, any>::value>
+        typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, any>::value>::type
     >
     any(T&& other) {
-        using contained_type = util::decay_t<T>;
+        using contained_type = typename std::decay<T>::type;
         static_assert(std::is_copy_constructible<contained_type>::value,
             "Type of contained object stored in any must satisfy the CopyConstructible requirements.");
 
@@ -62,10 +60,10 @@ public:
 
     template <
         typename T,
-        typename = typename util::enable_if_t<!std::is_same<util::decay_t<T>, any>::value>
+        typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, any>::value>::type
     >
     any& operator=(T&& other) {
-        using contained_type = util::decay_t<T>;
+        using contained_type = typename std::decay<T>::type;
 
         static_assert(std::is_copy_constructible<contained_type>::value,
             "Type of contained object stored in any must satisfy the CopyConstructible requirements.");
diff --git a/arbor/util/any_ptr.hpp b/include/arbor/util/any_ptr.hpp
similarity index 100%
rename from arbor/util/any_ptr.hpp
rename to include/arbor/util/any_ptr.hpp
diff --git a/arbor/util/make_unique.hpp b/include/arbor/util/make_unique.hpp
similarity index 74%
rename from arbor/util/make_unique.hpp
rename to include/arbor/util/make_unique.hpp
index 6b345c12..af0b80b0 100644
--- a/arbor/util/make_unique.hpp
+++ b/include/arbor/util/make_unique.hpp
@@ -5,8 +5,8 @@
 namespace arb {
 namespace util {
 
-// just because we aren't using C++14, doesn't mean we shouldn't go
-// without make_unique
+// TODO: Remove when migrate to C++14
+
 template <typename T, typename... Args>
 std::unique_ptr<T> make_unique(Args&&... args) {
     return std::unique_ptr<T>(new T(std::forward<Args>(args) ...));
diff --git a/arbor/util/unique_any.hpp b/include/arbor/util/unique_any.hpp
similarity index 91%
rename from arbor/util/unique_any.hpp
rename to include/arbor/util/unique_any.hpp
index 5091cc56..cae9edbe 100644
--- a/arbor/util/unique_any.hpp
+++ b/include/arbor/util/unique_any.hpp
@@ -4,8 +4,7 @@
 #include <typeinfo>
 #include <type_traits>
 
-#include <util/any.hpp>
-#include <util/meta.hpp>
+#include <arbor/util/any.hpp>
 
 // A non copyable variant of util::any.
 // The two main use cases for such a container are
@@ -57,11 +56,10 @@ public:
 
     template <
         typename T,
-        typename = typename util::enable_if_t<!std::is_same<util::decay_t<T>, unique_any>::value>
+        typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, unique_any>::value>::type
     >
     unique_any(T&& other) {
-        using contained_type = util::decay_t<T>;
-        state_.reset(new model<contained_type>(std::forward<T>(other)));
+        state_.reset(new model<contained_type<T>>(std::forward<T>(other)));
     }
 
     unique_any& operator=(unique_any&& other) noexcept {
@@ -71,11 +69,10 @@ public:
 
     template <
         typename T,
-        typename = typename util::enable_if_t<!std::is_same<util::decay_t<T>, unique_any>::value>
+        typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, unique_any>::value>::type
     >
     unique_any& operator=(T&& other) {
-        using contained_type = util::decay_t<T>;
-        state_.reset(new model<contained_type>(std::forward<T>(other)));
+        state_.reset(new model<contained_type<T>>(std::forward<T>(other)));
         return *this;
     }
 
@@ -96,6 +93,9 @@ public:
     }
 
 private:
+    template <typename T>
+    using contained_type = typename std::decay<T>::type;
+
     struct interface {
         virtual ~interface() = default;
         virtual const std::type_info& type() = 0;
diff --git a/lmorpho/lmorpho.cpp b/lmorpho/lmorpho.cpp
index 960981a1..b4f5492b 100644
--- a/lmorpho/lmorpho.cpp
+++ b/lmorpho/lmorpho.cpp
@@ -6,9 +6,9 @@
 #include <vector>
 
 #include <tinyopt.hpp>
+#include <arbor/morphology.hpp>
 #include <arbor/util/optional.hpp>
 
-#include "morphology.hpp"
 #include "morphio.hpp"
 #include "lsystem.hpp"
 #include "lsys_models.hpp"
diff --git a/lmorpho/lsystem.cpp b/lmorpho/lsystem.cpp
index a4baddd8..feddc70c 100644
--- a/lmorpho/lsystem.cpp
+++ b/lmorpho/lsystem.cpp
@@ -4,8 +4,9 @@
 #include <stack>
 #include <vector>
 
-#include <math.hpp>
-#include <morphology.hpp>
+#include <arbor/morphology.hpp>
+
+#include "math.hpp"
 
 #include "lsystem.hpp"
 
diff --git a/lmorpho/lsystem.hpp b/lmorpho/lsystem.hpp
index 1feb6f0f..55e5beda 100644
--- a/lmorpho/lsystem.hpp
+++ b/lmorpho/lsystem.hpp
@@ -2,7 +2,7 @@
 
 #include <random>
 
-#include <morphology.hpp>
+#include <arbor/morphology.hpp>
 
 struct lsys_param;
 
diff --git a/lmorpho/morphio.cpp b/lmorpho/morphio.cpp
index 9c354729..ee319dda 100644
--- a/lmorpho/morphio.cpp
+++ b/lmorpho/morphio.cpp
@@ -4,9 +4,10 @@
 #include <string>
 #include <vector>
 
-#include <morphology.hpp>
-#include <swcio.hpp>
-#include <util/strprintf.hpp>
+#include <arbor/morphology.hpp>
+
+#include "swcio.hpp"
+#include "util/strprintf.hpp"
 
 #include "morphio.hpp"
 
diff --git a/lmorpho/morphio.hpp b/lmorpho/morphio.hpp
index 0cc4f703..3d31dab7 100644
--- a/lmorpho/morphio.hpp
+++ b/lmorpho/morphio.hpp
@@ -4,7 +4,7 @@
 #include <iostream>
 #include <vector>
 
-#include <morphology.hpp>
+#include <arbor/morphology.hpp>
 
 // Manage access to a single file, std::cout, or an indexed
 // sequence of files.
diff --git a/modcc/printer/cprinter.cpp b/modcc/printer/cprinter.cpp
index d9ba01ac..6a0cc330 100644
--- a/modcc/printer/cprinter.cpp
+++ b/modcc/printer/cprinter.cpp
@@ -141,7 +141,7 @@ std::string emit_cpp_source(const Module& module_, const printer_options& opt) {
         out << "#include <" << arb_header_prefix() << "profile/profiler.hpp>\n";
 
     if (with_simd) {
-        out << "#include <" << arb_private_header_prefix() << "simd/simd.hpp>\n";
+        out << "#include <" << arb_header_prefix() << "simd/simd.hpp>\n";
     }
 
     out <<
diff --git a/test/common_cells.hpp b/test/common_cells.hpp
index 06ac14f3..3ced9dcb 100644
--- a/test/common_cells.hpp
+++ b/test/common_cells.hpp
@@ -1,11 +1,10 @@
 #include <cmath>
 
+#include <arbor/mc_cell.hpp>
+#include <arbor/mc_segment.hpp>
 #include <arbor/mechinfo.hpp>
 
-#include "cell.hpp"
 #include "recipe.hpp"
-#include "segment.hpp"
-#include "math.hpp"
 
 namespace arb {
 
@@ -22,8 +21,8 @@ namespace arb {
  *    soma centre, t=[10 ms, 110 ms), 0.1 nA
  */
 
-inline cell make_cell_soma_only(bool with_stim = true) {
-    cell c;
+inline mc_cell make_cell_soma_only(bool with_stim = true) {
+    mc_cell c;
 
     auto soma = c.add_soma(18.8/2.0);
     soma->add_mechanism("hh");
@@ -56,8 +55,8 @@ inline cell make_cell_soma_only(bool with_stim = true) {
  *    end of dendrite, t=[5 ms, 85 ms), 0.3 nA
  */
 
-inline cell make_cell_ball_and_stick(bool with_stim = true) {
-    cell c;
+inline mc_cell make_cell_ball_and_stick(bool with_stim = true) {
+    mc_cell c;
 
     auto soma = c.add_soma(12.6157/2.0);
     soma->add_mechanism("hh");
@@ -100,8 +99,8 @@ inline cell make_cell_ball_and_stick(bool with_stim = true) {
  *    end of dendrite, t=[5 ms, 85 ms), 0.3 nA
  */
 
-inline cell make_cell_ball_and_taper(bool with_stim = true) {
-    cell c;
+inline mc_cell make_cell_ball_and_taper(bool with_stim = true) {
+    mc_cell c;
 
     auto soma = c.add_soma(12.6157/2.0);
     soma->add_mechanism("hh");
@@ -142,14 +141,14 @@ inline cell make_cell_ball_and_taper(bool with_stim = true) {
  *    end of dendrite, t=[5 ms, 85 ms), 0.3 nA
  */
 
-inline cell make_cell_ball_and_squiggle(bool with_stim = true) {
-    cell c;
+inline mc_cell make_cell_ball_and_squiggle(bool with_stim = true) {
+    mc_cell c;
 
     auto soma = c.add_soma(12.6157/2.0);
     soma->add_mechanism("hh");
 
-    std::vector<cell::value_type> radii;
-    std::vector<cell::point_type> points;
+    std::vector<mc_cell::value_type> radii;
+    std::vector<mc_cell::point_type> points;
 
     double length = 100.0;
     int npoints = 200;
@@ -203,8 +202,8 @@ inline cell make_cell_ball_and_squiggle(bool with_stim = true) {
  *    end of second terminal branch, t=[40 ms, 50 ms), -0.2 nA
  */
 
-inline cell make_cell_ball_and_3stick(bool with_stim = true) {
-    cell c;
+inline mc_cell make_cell_ball_and_3stick(bool with_stim = true) {
+    mc_cell c;
 
     auto soma = c.add_soma(12.6157/2.0);
     soma->add_mechanism("hh");
@@ -248,8 +247,8 @@ inline cell make_cell_ball_and_3stick(bool with_stim = true) {
  * work-around for some existing fvm modelling issues.
  */
 
-inline cell make_cell_simple_cable(bool with_stim = true) {
-    cell c;
+inline mc_cell make_cell_simple_cable(bool with_stim = true) {
+    mc_cell c;
 
     c.add_soma(0);
     c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 1000);
@@ -275,7 +274,7 @@ inline cell make_cell_simple_cable(bool with_stim = true) {
     if (with_stim) {
         // stimulus in the middle of our zero-volume 'soma'
         // corresponds to proximal end of cable.
-        c.add_stimulus({0,0.5}, {0., math::infinity<>(), I});
+        c.add_stimulus({0,0.5}, {0., INFINITY, I});
     }
     return c;
 }
diff --git a/test/simple_recipes.hpp b/test/simple_recipes.hpp
index 9830dee0..531c66d8 100644
--- a/test/simple_recipes.hpp
+++ b/test/simple_recipes.hpp
@@ -5,7 +5,8 @@
 #include <unordered_map>
 #include <vector>
 
-#include <cell.hpp>
+#include <arbor/mc_cell.hpp>
+
 #include <event_generator.hpp>
 #include <recipe.hpp>
 
@@ -52,7 +53,7 @@ public:
 
 protected:
     std::unordered_map<cell_gid_type, std::vector<probe_info>> probes_;
-    cell_global_properties cell_gprop_;
+    mc_cell_global_properties cell_gprop_;
     mechanism_catalogue catalogue_;
 };
 
@@ -98,7 +99,7 @@ public:
         }
     }
 
-    explicit cable1d_recipe(const cell& c) {
+    explicit cable1d_recipe(const mc_cell& c) {
         cells_.reserve(1);
         cells_.emplace_back(c);
     }
@@ -115,11 +116,11 @@ public:
     }
 
     util::unique_any get_cell_description(cell_gid_type i) const override {
-        return util::make_unique_any<cell>(cells_[i]);
+        return util::make_unique_any<mc_cell>(cells_[i]);
     }
 
 protected:
-    std::vector<cell> cells_;
+    std::vector<mc_cell> cells_;
 };
 
 
diff --git a/test/ubench/mech_vec.cpp b/test/ubench/mech_vec.cpp
index 58c1643b..6f61d726 100644
--- a/test/ubench/mech_vec.cpp
+++ b/test/ubench/mech_vec.cpp
@@ -2,10 +2,13 @@
 //
 // Start with pas (passive dendrite) mechanism
 
+#include <fstream>
+
+#include <arbor/mc_cell.hpp>
+
 #include <backends/multicore/fvm.hpp>
 #include <benchmark/benchmark.h>
 #include <fvm_lowered_cell_impl.hpp>
-#include <fstream>
 
 using namespace arb;
 
@@ -36,7 +39,7 @@ public:
     }
 
     virtual util::unique_any get_cell_description(cell_gid_type gid) const override {
-        cell c;
+        mc_cell c;
 
         auto soma = c.add_soma(12.6157/2.0);
         soma->add_mechanism("pas");
@@ -75,7 +78,7 @@ public:
     }
 
     virtual util::unique_any get_cell_description(cell_gid_type gid) const override {
-        cell c;
+        mc_cell c;
 
         auto soma = c.add_soma(12.6157/2.0);
         soma->add_mechanism("pas");
@@ -107,7 +110,7 @@ public:
     }
 
     virtual util::unique_any get_cell_description(cell_gid_type gid) const override {
-        cell c;
+        mc_cell c;
 
         auto soma = c.add_soma(12.6157/2.0);
         soma->add_mechanism("pas");
@@ -141,7 +144,7 @@ public:
     }
 
     virtual util::unique_any get_cell_description(cell_gid_type gid) const override {
-        cell c;
+        mc_cell c;
 
         auto soma = c.add_soma(12.6157/2.0);
         soma->add_mechanism("hh");
@@ -173,7 +176,7 @@ public:
     }
 
     virtual util::unique_any get_cell_description(cell_gid_type gid) const override {
-        cell c;
+        mc_cell c;
 
         auto soma = c.add_soma(12.6157/2.0);
         soma->add_mechanism("pas");
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 094e03fd..887ffe53 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -26,7 +26,6 @@ set(unit_sources
     test_any.cpp
     test_backend.cpp
     test_double_buffer.cpp
-    test_cell.cpp
     test_compartments.cpp
     test_counter.cpp
     test_cycle.cpp
@@ -45,6 +44,7 @@ set(unit_sources
     test_mask_stream.cpp
     test_math.cpp
     test_matrix.cpp
+    test_mc_cell.cpp
     test_mechanisms.cpp
     test_mechcat.cpp
     test_merge_events.cpp
diff --git a/test/unit/test_any.cpp b/test/unit/test_any.cpp
index cce483f6..03dd829a 100644
--- a/test/unit/test_any.cpp
+++ b/test/unit/test_any.cpp
@@ -1,14 +1,12 @@
-//#include <iostream>
 #include <type_traits>
+#include <typeinfo>
+
+#include <arbor/util/any.hpp>
+#include <arbor/util/any_ptr.hpp>
 
 #include "../gtest.h"
 #include "common.hpp"
 
-#include <util/any.hpp>
-#include <util/any_ptr.hpp>
-
-#include <typeinfo>
-
 using namespace arb;
 using namespace testing::string_literals;
 
diff --git a/test/unit/test_compartments.cpp b/test/unit/test_compartments.cpp
index ac4503eb..9d5c711f 100644
--- a/test/unit/test_compartments.cpp
+++ b/test/unit/test_compartments.cpp
@@ -3,69 +3,18 @@
 
 #include "../gtest.h"
 
-#include <algorithms.hpp>
-#include <compartment.hpp>
-
-#include <math.hpp>
-#include <util/span.hpp>
-#include <util/transform.hpp>
+#include "algorithms.hpp"
+#include "fvm_compartment.hpp"
+#include "math.hpp"
+#include "util/span.hpp"
+#include "util/transform.hpp"
 
 using namespace arb;
-using namespace arb::algorithms;
 using namespace arb::math;
-using namespace arb::util;
-
-// not much to test here: just test that values passed into the constructor
-// are correctly stored in members
-TEST(compartments, compartment)
-{
-    {
-        arb::compartment c(100, 1.2, 2.1, 2.2);
-        EXPECT_EQ(c.index, 100u);
-        EXPECT_EQ(c.length, 1.2);
-        EXPECT_EQ(c.radius.first, 2.1);
-        EXPECT_EQ(c.radius.second, 2.2);
-
-        auto c2 = c;
-        EXPECT_EQ(c2.index, 100u);
-        EXPECT_EQ(c2.length, 1.2);
-        EXPECT_EQ(c2.radius.first, 2.1);
-        EXPECT_EQ(c2.radius.second, 2.2);
-    }
-
-    {
-        arb::compartment c{100, 1, 2, 3};
-        EXPECT_EQ(c.index, 100u);
-        EXPECT_EQ(c.length, 1.);
-        EXPECT_EQ(c.radius.first, 2.);
-        EXPECT_EQ(c.radius.second, 3.);
-    }
-}
-
-TEST(compartments, make_compartment_range)
-{
-    using namespace arb;
-    auto rng = make_compartment_range(10, 1.0, 2.0, 10.);
-
-    EXPECT_EQ((*rng.begin()).index, 0u);
-    EXPECT_EQ((*rng.end()).index, 10u);
-    EXPECT_NE(rng.begin(), rng.end());
-
-    unsigned count = 0;
-    for (auto c : rng) {
-        EXPECT_EQ(c.index, count);
-        auto er = 1.0 + double(count)/10.;
-        EXPECT_DOUBLE_EQ(c.radius.first, er);
-        EXPECT_DOUBLE_EQ(c.radius.second, er+0.1);
-        EXPECT_EQ(c.length, 1.0);
-        ++count;
-    }
-    EXPECT_EQ(count, 10u);
 
-    // test case of zero length range
-    auto rng_empty = make_compartment_range(0, 1.0, 1.0, 0.);
-    EXPECT_EQ(rng_empty.begin(), rng_empty.end());
-}
+using arb::util::make_span;
+using arb::util::transform_view;
+using arb::algorithms::sum;
 
 // Divided compartments
 // (FVM-friendly compartment data)
@@ -81,12 +30,12 @@ struct pw_cable_data {
     double length() const { return sum(lengths); }
 
     double area() const {
-        return sum(transform_view(make_span(0, N),
+        return sum(transform_view(make_span(N),
             [&](unsigned i) { return area_frustrum(lengths[i], radii[i], radii[i+1]); }));
     }
 
     double volume() const {
-        return sum(transform_view(make_span(0, N),
+        return sum(transform_view(make_span(N),
             [&](unsigned i) { return volume_frustrum(lengths[i], radii[i], radii[i+1]); }));
     }
 };
@@ -128,8 +77,6 @@ void expect_equal_divs(const div_compartment& da, const div_compartment& db) {
 }
 
 TEST(compartments, div_ends) {
-    using namespace math;
-
     {
         div_compartment_by_ends divcomps{1, cable_one.radii, cable_one.lengths};
 
@@ -183,8 +130,6 @@ TEST(compartments, div_ends) {
 }
 
 TEST(compartments, div_sample) {
-    using namespace math;
-
     // expect by_ends and sampler to give same results on linear cable
     {
         constexpr unsigned ncomp = 7;
@@ -223,10 +168,10 @@ TEST(compartments, div_sample) {
             unsigned ncomp = m*nbase;
             div_compartment_sampler divs{ncomp, cable_jumble.radii, cable_jumble.lengths};
 
-            double area = sum(transform_view(make_span(0, ncomp),
+            double area = sum(transform_view(make_span(ncomp),
                 [&](unsigned i) { return divs(i).area(); }));
 
-            double volume = sum(transform_view(make_span(0, ncomp),
+            double volume = sum(transform_view(make_span(ncomp),
                 [&](unsigned i) { return divs(i).volume(); }));
 
             double e2 = std::min(area, area_expected)*ncomp*eps;
@@ -247,10 +192,10 @@ TEST(compartments, div_sample) {
             unsigned ncomp = m*nbase+1u;
             div_compartment_sampler divs{ncomp, cable_jumble.radii, cable_jumble.lengths};
 
-            double area = sum(transform_view(make_span(0, ncomp),
+            double area = sum(transform_view(make_span(ncomp),
                 [&](unsigned i) { return divs(i).area(); }));
 
-            double volume = sum(transform_view(make_span(0, ncomp),
+            double volume = sum(transform_view(make_span(ncomp),
                 [&](unsigned i) { return divs(i).volume(); }));
 
             SCOPED_TRACE("cable_jumble ncomp "+std::to_string(ncomp));
@@ -267,8 +212,6 @@ TEST(compartments, div_sample) {
 }
 
 TEST(compartments, div_integrator) {
-    using namespace math;
-
     // expect integrator and sampler to give same results on linear cable
     {
         constexpr unsigned ncomp = 7;
@@ -296,10 +239,10 @@ TEST(compartments, div_integrator) {
         for (unsigned ncomp: make_span(1u, 23u)) {
             div_compartment_integrator divs{ncomp, cable_jumble.radii, cable_jumble.lengths};
 
-            double area = sum(transform_view(make_span(0, ncomp),
+            double area = sum(transform_view(make_span(ncomp),
                 [&](unsigned i) { return divs(i).area(); }));
 
-            double volume = sum(transform_view(make_span(0, ncomp),
+            double volume = sum(transform_view(make_span(ncomp),
                 [&](unsigned i) { return divs(i).volume(); }));
 
             double e2 = std::min(area, area_expected)*ncomp*eps;
diff --git a/test/unit/test_domain_decomposition.cpp b/test/unit/test_domain_decomposition.cpp
index 0dae331c..16dd8785 100644
--- a/test/unit/test_domain_decomposition.cpp
+++ b/test/unit/test_domain_decomposition.cpp
@@ -8,10 +8,12 @@
 #include "domain_decomposition.hpp"
 #include "hardware/node_info.hpp"
 #include "load_balance.hpp"
+#include "util/span.hpp"
 
 #include "../simple_recipes.hpp"
 
 using namespace arb;
+using arb::util::make_span;
 
 namespace {
     // Dummy recipes types for testing.
@@ -63,7 +65,7 @@ TEST(domain_decomposition, homogenous_population)
         EXPECT_EQ(D.num_local_cells, num_cells);
         EXPECT_EQ(D.groups.size(), num_cells);
 
-        auto gids = util::make_span(0, num_cells);
+        auto gids = make_span(num_cells);
         for (auto gid: gids) {
             EXPECT_EQ(0, D.gid_domain(gid));
         }
@@ -89,7 +91,7 @@ TEST(domain_decomposition, homogenous_population)
         EXPECT_EQ(D.num_local_cells, num_cells);
         EXPECT_EQ(D.groups.size(), 1u);
 
-        auto gids = util::make_span(0, num_cells);
+        auto gids = make_span(num_cells);
         for (auto gid: gids) {
             EXPECT_EQ(0, D.gid_domain(gid));
         }
@@ -124,14 +126,14 @@ TEST(domain_decomposition, heterogenous_population)
         EXPECT_EQ(D.num_local_cells, num_cells);
         EXPECT_EQ(D.groups.size(), num_cells);
 
-        auto gids = util::make_span(0, num_cells);
+        auto gids = make_span(num_cells);
         for (auto gid: gids) {
             EXPECT_EQ(0, D.gid_domain(gid));
         }
 
         // Each cell group contains 1 cell of kind cable1d_neuron
         // Each group should also be tagged for cpu execution
-        auto grps = util::make_span(0, num_cells);
+        auto grps = make_span(num_cells);
         std::map<cell_kind, std::set<cell_gid_type>> kind_lists;
         for (auto i: grps) {
             auto& grp = D.groups[i];
@@ -164,7 +166,7 @@ TEST(domain_decomposition, heterogenous_population)
         auto expected_groups = num_cells/2+1;
         EXPECT_EQ(D.groups.size(), expected_groups);
 
-        auto grps = util::make_span(0, expected_groups);
+        auto grps = make_span(expected_groups);
         unsigned ncells = 0;
         // iterate over each group and test its properties
         for (auto i: grps) {
diff --git a/test/unit/test_fvm_layout.cpp b/test/unit/test_fvm_layout.cpp
index d5a96bb5..2389e9f6 100644
--- a/test/unit/test_fvm_layout.cpp
+++ b/test/unit/test_fvm_layout.cpp
@@ -2,8 +2,8 @@
 
 #include <arbor/util/optional.hpp>
 #include <arbor/mechcat.hpp>
+#include <arbor/mc_cell.hpp>
 
-#include "cell.hpp"
 #include "fvm_layout.hpp"
 #include "math.hpp"
 #include "util/maputil.hpp"
@@ -20,80 +20,116 @@ using util::make_span;
 using util::count_along;
 using util::value_by_key;
 
-std::vector<cell> two_cell_system() {
-    std::vector<cell> cells;
-
-    // Cell 0: simple ball and stick (see common_cells.hpp)
-    cells.push_back(make_cell_ball_and_stick());
-
-    // Cell 1: ball and 3-stick, but with uneven dendrite
-    // length and heterogeneous electrical properties:
-    //
-    // Bulk resistivity: 90 Ω·cm
-    // capacitance:
-    //    soma:       0.01  F/m² [default]
-    //    segment 1:  0.017 F/m²
-    //    segment 2:  0.013 F/m²
-    //    segment 3:  0.018 F/m²
-    //
-    // Soma diameter: 14 µm
-    // Some mechanisms: HH (default params)
-    //
-    // Segment 1 diameter: 1 µm
-    // Segment 1 length:   200 µm
-    //
-    // Segment 2 diameter: 0.8 µm
-    // Segment 2 length:   300 µm
-    //
-    // Segment 3 diameter: 0.7 µm
-    // Segment 3 length:   180 µm
-    //
-    // Dendrite mechanisms: passive (default params).
-    // Stimulus at end of segment 2, amplitude 0.45.
-    // Stimulus at end of segment 3, amplitude -0.2.
-    //
-    // All dendrite segments with 4 compartments.
-
-    cell c2;
-    segment* s;
-
-    s = c2.add_soma(14./2);
-    s->add_mechanism("hh");
-
-    s = c2.add_cable(0, section_kind::dendrite, 1.0/2, 1.0/2, 200);
-    s->cm = 0.017;
-
-    s = c2.add_cable(1, section_kind::dendrite, 0.8/2, 0.8/2, 300);
-    s->cm = 0.013;
-
-    s = c2.add_cable(1, section_kind::dendrite, 0.7/2, 0.7/2, 180);
-    s->cm = 0.018;
+namespace {
+    double area(const mc_segment* s) {
+        if (auto soma = s->as_soma()) {
+            return math::area_sphere(soma->radius());
+        }
+        else if (auto cable = s->as_cable()) {
+            unsigned nc = cable->num_sub_segments();
+            double a = 0;
+            for (unsigned i = 0; i<nc; ++i) {
+                a += math::area_frustrum(cable->lengths()[i], cable->radii()[i], cable->radii()[i+1]);
+            }
+            return a;
+        }
+        else {
+            return 0;
+        }
+    }
 
-    c2.add_stimulus({2,1}, {5.,  80., 0.45});
-    c2.add_stimulus({3,1}, {40., 10.,-0.2});
+    double volume(const mc_segment* s) {
+        if (auto soma = s->as_soma()) {
+            return math::volume_sphere(soma->radius());
+        }
+        else if (auto cable = s->as_cable()) {
+            unsigned nc = cable->num_sub_segments();
+            double v = 0;
+            for (unsigned i = 0; i<nc; ++i) {
+                v += math::volume_frustrum(cable->lengths()[i], cable->radii()[i], cable->radii()[i+1]);
+            }
+            return v;
+        }
+        else {
+            return 0;
+        }
+    }
 
-    for (auto& seg: c2.segments()) {
-        seg->rL = 90.;
-        if (seg->is_dendrite()) {
-            seg->add_mechanism("pas");
-            seg->set_compartments(4);
+    std::vector<mc_cell> two_cell_system() {
+        std::vector<mc_cell> cells;
+
+        // Cell 0: simple ball and stick (see common_cells.hpp)
+        cells.push_back(make_cell_ball_and_stick());
+
+        // Cell 1: ball and 3-stick, but with uneven dendrite
+        // length and heterogeneous electrical properties:
+        //
+        // Bulk resistivity: 90 Ω·cm
+        // capacitance:
+        //    soma:       0.01  F/m² [default]
+        //    segment 1:  0.017 F/m²
+        //    segment 2:  0.013 F/m²
+        //    segment 3:  0.018 F/m²
+        //
+        // Soma diameter: 14 µm
+        // Some mechanisms: HH (default params)
+        //
+        // Segment 1 diameter: 1 µm
+        // Segment 1 length:   200 µm
+        //
+        // Segment 2 diameter: 0.8 µm
+        // Segment 2 length:   300 µm
+        //
+        // Segment 3 diameter: 0.7 µm
+        // Segment 3 length:   180 µm
+        //
+        // Dendrite mechanisms: passive (default params).
+        // Stimulus at end of segment 2, amplitude 0.45.
+        // Stimulus at end of segment 3, amplitude -0.2.
+        //
+        // All dendrite segments with 4 compartments.
+
+        mc_cell c2;
+        mc_segment* s;
+
+        s = c2.add_soma(14./2);
+        s->add_mechanism("hh");
+
+        s = c2.add_cable(0, section_kind::dendrite, 1.0/2, 1.0/2, 200);
+        s->cm = 0.017;
+
+        s = c2.add_cable(1, section_kind::dendrite, 0.8/2, 0.8/2, 300);
+        s->cm = 0.013;
+
+        s = c2.add_cable(1, section_kind::dendrite, 0.7/2, 0.7/2, 180);
+        s->cm = 0.018;
+
+        c2.add_stimulus({2,1}, {5.,  80., 0.45});
+        c2.add_stimulus({3,1}, {40., 10.,-0.2});
+
+        for (auto& seg: c2.segments()) {
+            seg->rL = 90.;
+            if (seg->is_dendrite()) {
+                seg->add_mechanism("pas");
+                seg->set_compartments(4);
+            }
         }
+        cells.push_back(std::move(c2));
+        return cells;
     }
-    cells.push_back(std::move(c2));
-    return cells;
-}
 
-void check_two_cell_system(std::vector<cell>& cells) {
-    ASSERT_EQ(2u, cells[0].num_segments());
-    ASSERT_EQ(cells[0].segment(1)->num_compartments(), 4u);
-    ASSERT_EQ(cells[1].num_segments(), 4u);
-    ASSERT_EQ(cells[1].segment(1)->num_compartments(), 4u);
-    ASSERT_EQ(cells[1].segment(2)->num_compartments(), 4u);
-    ASSERT_EQ(cells[1].segment(3)->num_compartments(), 4u);
-}
+    void check_two_cell_system(std::vector<mc_cell>& cells) {
+        ASSERT_EQ(2u, cells[0].num_segments());
+        ASSERT_EQ(cells[0].segment(1)->num_compartments(), 4u);
+        ASSERT_EQ(cells[1].num_segments(), 4u);
+        ASSERT_EQ(cells[1].segment(1)->num_compartments(), 4u);
+        ASSERT_EQ(cells[1].segment(2)->num_compartments(), 4u);
+        ASSERT_EQ(cells[1].segment(3)->num_compartments(), 4u);
+    }
+} // namespace
 
 TEST(fvm_layout, topology) {
-    std::vector<cell> cells = two_cell_system();
+    std::vector<mc_cell> cells = two_cell_system();
     check_two_cell_system(cells);
 
     fvm_discretization D = fvm_discretize(cells);
@@ -173,7 +209,7 @@ TEST(fvm_layout, topology) {
 }
 
 TEST(fvm_layout, area) {
-    std::vector<cell> cells = two_cell_system();
+    std::vector<mc_cell> cells = two_cell_system();
     check_two_cell_system(cells);
 
     fvm_discretization D = fvm_discretize(cells);
@@ -184,7 +220,7 @@ TEST(fvm_layout, area) {
     std::vector<double> A;
     for (auto ci: make_span(D.ncell)) {
         for (auto si: make_span(cells[ci].num_segments())) {
-            A.push_back(cells[ci].segment(si)->area());
+            A.push_back(area(cells[ci].segment(si)));
         }
     }
 
@@ -230,12 +266,12 @@ TEST(fvm_layout, area) {
     EXPECT_FLOAT_EQ(c, D.cv_capacitance[5]);
 
     // Confirm face conductance within a constant diameter
-    // segment equals a/h·1/rL where a is the cross sectional
+    // equals a/h·1/rL where a is the cross sectional
     // area, and h is the compartment length (given the
     // regular discretization).
 
     cable_segment* cable = cells[1].segment(2)->as_cable();
-    double a = cable->volume()/cable->length();
+    double a = volume(cable)/cable->length();
     EXPECT_FLOAT_EQ(math::pi<double>()*0.8*0.8/4, a);
 
     double h = cable->length()/4;
@@ -246,7 +282,7 @@ TEST(fvm_layout, area) {
 }
 
 TEST(fvm_layout, mech_index) {
-    std::vector<cell> cells = two_cell_system();
+    std::vector<mc_cell> cells = two_cell_system();
     check_two_cell_system(cells);
 
     // Add four synapses of two varieties across the cells.
@@ -271,7 +307,7 @@ TEST(fvm_layout, mech_index) {
     EXPECT_EQ(mechanismKind::density, hh_config.kind);
     EXPECT_EQ(ivec({0,5}), hh_config.cv);
 
-    fvec norm_area({cells[0].soma()->area()/D.cv_area[0], cells[1].soma()->area()/D.cv_area[5]});
+    fvec norm_area({area(cells[0].soma())/D.cv_area[0], area(cells[1].soma())/D.cv_area[5]});
     EXPECT_TRUE(testing::seq_almost_eq<double>(norm_area, hh_config.norm_area));
 
     // Three expsyn synapses, two 0.4 along segment 1, and one 0.4 along segment 5.
@@ -295,7 +331,7 @@ TEST(fvm_layout, mech_index) {
 }
 
 TEST(fvm_layout, synapse_targets) {
-    std::vector<cell> cells = two_cell_system();
+    std::vector<mc_cell> cells = two_cell_system();
 
     // Add synapses with different parameter values so that we can
     // ensure: 1) CVs for each synapse mechanism are sorted while
@@ -404,8 +440,8 @@ TEST(fvm_layout, density_norm_area) {
     //
     // Use divided compartment view on segments to compute area contributions.
 
-    std::vector<cell> cells(1);
-    cell& c = cells[0];
+    std::vector<mc_cell> cells(1);
+    mc_cell& c = cells[0];
     auto soma = c.add_soma(12.6157/2.0);
 
     c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100);
@@ -423,7 +459,7 @@ TEST(fvm_layout, density_norm_area) {
     double seg3_gl = .0004;
 
     for (int i = 0; i<4; ++i) {
-        segment& seg = *segs[i];
+        mc_segment& seg = *segs[i];
         seg.set_compartments(3);
 
         mechanism_desc hh("hh");
@@ -447,10 +483,13 @@ TEST(fvm_layout, density_norm_area) {
     std::vector<double> expected_gkbar(ncv, dflt_gkbar);
     std::vector<double> expected_gl(ncv, dflt_gl);
 
-    double soma_area = soma->area();
-    auto seg1_divs = div_compartments<div_compartment_by_ends>(segs[1]->as_cable());
-    auto seg2_divs = div_compartments<div_compartment_by_ends>(segs[2]->as_cable());
-    auto seg3_divs = div_compartments<div_compartment_by_ends>(segs[3]->as_cable());
+    auto div_by_ends = [](const cable_segment* cable) {
+        return div_compartment_by_ends(cable->num_compartments(), cable->radii(), cable->lengths());
+    };
+    double soma_area = area(soma);
+    auto seg1_divs = div_by_ends(segs[1]->as_cable());
+    auto seg2_divs = div_by_ends(segs[2]->as_cable());
+    auto seg3_divs = div_by_ends(segs[3]->as_cable());
 
     // CV 0: mix of soma and left of segment 1
     expected_gl[0] = wmean(soma_area, dflt_gl, seg1_divs(0).left.area, seg1_gl);
@@ -531,7 +570,7 @@ TEST(fvm_layout, ion_weights) {
     // the same as a 100µm dendrite, which makes it easier to describe the
     // expected weights.
 
-    auto construct_cell = [](cell& c) {
+    auto construct_cell = [](mc_cell& c) {
         c.add_soma(5);
 
         c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100);
@@ -558,8 +597,8 @@ TEST(fvm_layout, ion_weights) {
     };
 
     for (auto run: count_along(mech_segs)) {
-        std::vector<cell> cells(1);
-        cell& c = cells[0];
+        std::vector<mc_cell> cells(1);
+        mc_cell& c = cells[0];
         construct_cell(c);
 
         for (auto i: mech_segs[run]) {
diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp
index c30bf57d..1a23165b 100644
--- a/test/unit/test_fvm_lowered.cpp
+++ b/test/unit/test_fvm_lowered.cpp
@@ -5,11 +5,13 @@
 #include <arbor/common_types.hpp>
 #include <arbor/distributed_context.hpp>
 #include <arbor/fvm_types.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/mc_segment.hpp>
+#include <arbor/sampling.hpp>
 
 #include "algorithms.hpp"
 #include "backends/multicore/fvm.hpp"
 #include "backends/multicore/mechanism.hpp"
-#include "cell.hpp"
 #include "fvm_lowered_cell.hpp"
 #include "fvm_lowered_cell_impl.hpp"
 #include "load_balance.hpp"
@@ -17,9 +19,7 @@
 #include "simulation.hpp"
 #include "recipe.hpp"
 #include "sampler_map.hpp"
-#include "sampling.hpp"
 #include "schedule.hpp"
-#include "segment.hpp"
 #include "util/meta.hpp"
 #include "util/maputil.hpp"
 #include "util/rangeutil.hpp"
@@ -87,7 +87,7 @@ TEST(fvm_lowered, matrix_init)
     algorithms::generic_is_positive ispos;
     algorithms::generic_is_negative isneg;
 
-    arb::cell cell = make_cell_ball_and_stick();
+    mc_cell cell = make_cell_ball_and_stick();
 
     ASSERT_EQ(2u, cell.num_segments());
     cell.segment(1)->set_compartments(10);
@@ -119,7 +119,7 @@ TEST(fvm_lowered, matrix_init)
 TEST(fvm_lowered, target_handles) {
     using namespace arb;
 
-    arb::cell cells[] = {
+    mc_cell cells[] = {
         make_cell_ball_and_stick(),
         make_cell_ball_and_3stick()
     };
@@ -178,7 +178,7 @@ TEST(fvm_lowered, stimulus) {
     // amplitude | 0.3  |  0.1
     // CV        |   4  |    0
 
-    std::vector<cell> cells;
+    std::vector<mc_cell> cells;
     cells.push_back(make_cell_ball_and_stick(false));
 
     cells[0].add_stimulus({1,1},   {5., 80., 0.3});
@@ -253,9 +253,9 @@ TEST(fvm_lowered, derived_mechs) {
     //
     // 3. Cell with both test_kin1 and custom_kin1.
 
-    std::vector<cell> cells(3);
+    std::vector<mc_cell> cells(3);
     for (int i = 0; i<3; ++i) {
-        cell& c = cells[i];
+        mc_cell& c = cells[i];
         c.add_soma(6.0);
         c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100);
 
@@ -376,7 +376,7 @@ TEST(fvm_lowered, weighted_write_ion) {
     // the same as a 100µm dendrite, which makes it easier to describe the
     // expected weights.
 
-    cell c;
+    mc_cell c;
     c.add_soma(5);
 
     c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100);
diff --git a/test/unit/test_lif_cell_group.cpp b/test/unit/test_lif_cell_group.cpp
index 385ce7c7..40bc2c38 100644
--- a/test/unit/test_lif_cell_group.cpp
+++ b/test/unit/test_lif_cell_group.cpp
@@ -1,16 +1,15 @@
 #include "../gtest.h"
 
 #include <arbor/distributed_context.hpp>
+#include <arbor/lif_cell.hpp>
 #include <arbor/threadinfo.hpp>
+#include <arbor/spike_source_cell.hpp>
 
-#include <cell_group_factory.hpp>
-#include <fstream>
-#include <lif_cell_description.hpp>
-#include <lif_cell_group.hpp>
-#include <load_balance.hpp>
-#include <simulation.hpp>
-#include <spike_source_cell.hpp>
-#include <recipe.hpp>
+#include "cell_group_factory.hpp"
+#include "lif_cell_group.hpp"
+#include "load_balance.hpp"
+#include "simulation.hpp"
+#include "recipe.hpp"
 
 using namespace arb;
 // Simple ring network of LIF neurons.
@@ -65,7 +64,7 @@ public:
             return spike_source_cell{vector_time_seq({0.f})};
         }
         // LIF cell.
-        return lif_cell_description();
+        return lif_cell();
     }
 
     cell_size_type num_sources(cell_gid_type) const override {
@@ -118,7 +117,7 @@ public:
     }
 
     util::unique_any get_cell_description(cell_gid_type gid) const override {
-        return lif_cell_description();
+        return lif_cell();
     }
 
     cell_size_type num_sources(cell_gid_type) const override {
diff --git a/test/unit/test_cell.cpp b/test/unit/test_mc_cell.cpp
similarity index 80%
rename from test/unit/test_cell.cpp
rename to test/unit/test_mc_cell.cpp
index fee64731..98e1b5e3 100644
--- a/test/unit/test_cell.cpp
+++ b/test/unit/test_mc_cell.cpp
@@ -1,13 +1,17 @@
 #include "../gtest.h"
 
-#include "cell.hpp"
+#include <arbor/mc_cell.hpp>
 
-TEST(cell, soma)
-{
+#include "math.hpp"
+#include "tree.hpp"
+
+using namespace arb;
+
+TEST(mc_cell, soma) {
     // test that insertion of a soma works
     //      define with no centre point
     {
-        arb::cell c;
+        mc_cell c;
         auto soma_radius = 2.1;
 
         EXPECT_EQ(c.has_soma(), false);
@@ -22,7 +26,7 @@ TEST(cell, soma)
     // test that insertion of a soma works
     //      define with centre point @ (0,0,1)
     {
-        arb::cell c;
+        mc_cell c;
         auto soma_radius = 3.2;
 
         EXPECT_EQ(c.has_soma(), false);
@@ -38,12 +42,10 @@ TEST(cell, soma)
     }
 }
 
-TEST(cell, add_segment)
-{
-    using namespace arb;
+TEST(mc_cell, add_segment) {
     //  add a pre-defined segment
     {
-        cell c;
+        mc_cell c;
 
         auto soma_radius  = 2.1;
         auto cable_radius = 0.1;
@@ -64,7 +66,7 @@ TEST(cell, add_segment)
 
     //  add segment on the fly
     {
-        cell c;
+        mc_cell c;
 
         auto soma_radius  = 2.1;
         auto cable_radius = 0.1;
@@ -81,7 +83,7 @@ TEST(cell, add_segment)
         EXPECT_EQ(c.num_segments(), 2u);
     }
     {
-        cell c;
+        mc_cell c;
 
         auto soma_radius  = 2.1;
         auto cable_radius = 0.1;
@@ -101,10 +103,7 @@ TEST(cell, add_segment)
     }
 }
 
-TEST(cell, multiple_cables)
-{
-    using namespace arb;
-
+TEST(mc_cell, multiple_cables) {
     // generate a cylindrical cable segment of length 1/pi and radius 1
     //      volume = 1
     //      area   = 2
@@ -114,7 +113,7 @@ TEST(cell, multiple_cables)
 
     //  add a pre-defined segment
     {
-        cell c;
+        mc_cell c;
 
         auto soma_radius = std::pow(3./(4.*math::pi<double>()), 1./3.);
 
@@ -138,15 +137,9 @@ TEST(cell, multiple_cables)
         c.add_cable(1, seg(section_kind::dendrite));
 
         EXPECT_EQ(c.num_segments(), 5u);
-        // each of the 5 segments has volume 1 by design
-        EXPECT_EQ(c.volume(), 5.);
-        // each of the 4 cables has volume 2., and the soma has an awkward area
-        // that isn't a round number
-        EXPECT_EQ(c.area(), 8. + math::area_sphere(soma_radius));
 
         // construct the graph
-        const auto model = c.model();
-        auto const& con = model.tree;
+        tree con(c.parents());
 
         auto no_parent = tree::no_parent;
         EXPECT_EQ(con.num_segments(), 5u);
@@ -164,11 +157,8 @@ TEST(cell, multiple_cables)
     }
 }
 
-TEST(cell, unbranched_chain)
-{
-    using namespace arb;
-
-    cell c;
+TEST(mc_cell, unbranched_chain) {
+    mc_cell c;
 
     auto soma_radius = std::pow(3./(4.*math::pi<double>()), 1./3.);
 
@@ -186,32 +176,24 @@ TEST(cell, unbranched_chain)
     c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 1.0, 1.0, 1./math::pi<double>()));
 
     EXPECT_EQ(c.num_segments(), 3u);
-    // each of the 3 segments has volume 1 by design
-    EXPECT_EQ(c.volume(), 3.);
-    // each of the 2 cables has volume 2., and the soma has an awkward area
-    // that isn't a round number
-    EXPECT_EQ(c.area(), 4. + math::area_sphere(soma_radius));
 
     // construct the graph
-    const auto tree = c.model().tree;
+    tree con(c.parents());
 
     auto no_parent = tree::no_parent;
-    EXPECT_EQ(tree.num_segments(), 3u);
-    EXPECT_EQ(tree.parent(0), no_parent);
-    EXPECT_EQ(tree.parent(1), 0u);
-    EXPECT_EQ(tree.parent(2), 1u);
-    EXPECT_EQ(tree.num_children(0), 1u);
-    EXPECT_EQ(tree.num_children(1), 1u);
-    EXPECT_EQ(tree.num_children(2), 0u);
+    EXPECT_EQ(con.num_segments(), 3u);
+    EXPECT_EQ(con.parent(0), no_parent);
+    EXPECT_EQ(con.parent(1), 0u);
+    EXPECT_EQ(con.parent(2), 1u);
+    EXPECT_EQ(con.num_children(0), 1u);
+    EXPECT_EQ(con.num_children(1), 1u);
+    EXPECT_EQ(con.num_children(2), 0u);
 }
 
-TEST(cell, clone)
-{
-    using namespace arb;
-
+TEST(mc_cell, clone) {
     // make simple cell with multiple segments
 
-    cell c;
+    mc_cell c;
     c.add_soma(2.1);
     c.add_cable(0, section_kind::dendrite, 0.3, 0.2, 10);
     c.segment(1)->set_compartments(3);
@@ -224,7 +206,7 @@ TEST(cell, clone)
 
     // make clone
 
-    cell d(c);
+    mc_cell d(c);
 
     // check equality
 
@@ -260,11 +242,7 @@ TEST(cell, clone)
     EXPECT_EQ(c.segment(2)->num_compartments(), d.segment(2)->num_compartments());
 }
 
-TEST(cell, get_kind)
-{
-    using namespace arb;
-
-    // make a MC cell
-    cell c;
-    EXPECT_EQ( cell_kind::cable1d_neuron, c.get_cell_kind());
+TEST(mc_cell, get_kind) {
+    mc_cell c;
+    EXPECT_EQ(cell_kind::cable1d_neuron, c.get_cell_kind());
 }
diff --git a/test/unit/test_mc_cell_group.cpp b/test/unit/test_mc_cell_group.cpp
index 0a0c7d1d..4aaf42eb 100644
--- a/test/unit/test_mc_cell_group.cpp
+++ b/test/unit/test_mc_cell_group.cpp
@@ -19,7 +19,7 @@ namespace {
         return make_fvm_lowered_cell(backend_kind::multicore);
     }
 
-    cell make_cell() {
+    mc_cell make_cell() {
         auto c = make_cell_ball_and_stick();
 
         c.add_detector({0, 0}, 0);
@@ -52,7 +52,7 @@ TEST(mc_cell_group, test) {
 TEST(mc_cell_group, sources) {
     // Make twenty cells, with an extra detector on gids 0, 3 and 17
     // to make things more interesting.
-    std::vector<cell> cells;
+    std::vector<mc_cell> cells;
 
     for (int i=0; i<20; ++i) {
         cells.push_back(make_cell());
diff --git a/test/unit/test_mc_cell_group_gpu.cpp b/test/unit/test_mc_cell_group_gpu.cpp
index 8b357510..f3662484 100644
--- a/test/unit/test_mc_cell_group_gpu.cpp
+++ b/test/unit/test_mc_cell_group_gpu.cpp
@@ -17,7 +17,7 @@ namespace {
         return make_fvm_lowered_cell(backend_kind::gpu);
     }
 
-    cell make_cell() {
+    mc_cell make_cell() {
         auto c = make_cell_ball_and_stick();
 
         c.add_detector({0, 0}, 0);
diff --git a/test/unit/test_mechinfo.cpp b/test/unit/test_mechinfo.cpp
index b5995d11..e5f63588 100644
--- a/test/unit/test_mechinfo.cpp
+++ b/test/unit/test_mechinfo.cpp
@@ -2,7 +2,7 @@
 #include <string>
 #include <vector>
 
-#include <cell.hpp>
+#include <arbor/mc_cell.hpp>
 
 #include "../gtest.h"
 
diff --git a/test/unit/test_partition_by_constraint.cpp b/test/unit/test_partition_by_constraint.cpp
index 14945327..733bb941 100644
--- a/test/unit/test_partition_by_constraint.cpp
+++ b/test/unit/test_partition_by_constraint.cpp
@@ -6,10 +6,10 @@
 #include <vector>
 
 #include <arbor/common_types.hpp>
+#include <arbor/simd/simd.hpp>
 
-#include <simd/simd.hpp>
-#include <backends/multicore/multicore_common.hpp>
-#include <backends/multicore/partition_by_constraint.hpp>
+#include "backends/multicore/multicore_common.hpp"
+#include "backends/multicore/partition_by_constraint.hpp"
 
 using namespace arb;
 using iarray = multicore::iarray;
diff --git a/test/unit/test_point.cpp b/test/unit/test_point.cpp
index 2edb08ed..872ec6dd 100644
--- a/test/unit/test_point.cpp
+++ b/test/unit/test_point.cpp
@@ -3,7 +3,7 @@
 
 #include "../gtest.h"
 
-#include "point.hpp"
+#include <arbor/point.hpp>
 
 using namespace arb;
 
diff --git a/test/unit/test_probe.cpp b/test/unit/test_probe.cpp
index 6064a02b..b22df63b 100644
--- a/test/unit/test_probe.cpp
+++ b/test/unit/test_probe.cpp
@@ -1,12 +1,12 @@
 #include "../gtest.h"
 
 #include <arbor/common_types.hpp>
+#include <arbor/mc_cell.hpp>
 
-#include <backends/event.hpp>
-#include <backends/multicore/fvm.hpp>
-#include <cell.hpp>
-#include <fvm_lowered_cell_impl.hpp>
-#include <util/rangeutil.hpp>
+#include "backends/event.hpp"
+#include "backends/multicore/fvm.hpp"
+#include "fvm_lowered_cell_impl.hpp"
+#include "util/rangeutil.hpp"
 
 #include "common.hpp"
 #include "../common_cells.hpp"
@@ -19,7 +19,7 @@ using shared_state = multicore::backend::shared_state;
 ACCESS_BIND(std::unique_ptr<shared_state> fvm_cell::*, fvm_state_ptr, &fvm_cell::state_);
 
 TEST(probe, fvm_lowered_cell) {
-    cell bs = make_cell_ball_and_stick(false);
+    mc_cell bs = make_cell_ball_and_stick(false);
 
     i_clamp stim(0, 100, 0.3);
     bs.add_stimulus({1, 1}, stim);
diff --git a/test/unit/test_segment.cpp b/test/unit/test_segment.cpp
index fc984ee4..ed6022e8 100644
--- a/test/unit/test_segment.cpp
+++ b/test/unit/test_segment.cpp
@@ -1,47 +1,33 @@
-#include <limits>
+#include <vector>
 
 #include "../gtest.h"
 
-#include "segment.hpp"
+#include <arbor/mc_segment.hpp>
 
-TEST(segments, soma)
-{
+#include "math.hpp"
+
+using namespace arb;
+
+TEST(mc_segment, kinfs) {
     using namespace arb;
     using arb::math::pi;
 
     {
         auto s = make_segment<soma_segment>(1.0);
-
-        EXPECT_EQ(s->volume(), pi<double>()*4./3.);
-        EXPECT_EQ(s->area(),   pi<double>()*4.);
         EXPECT_EQ(s->kind(),   section_kind::soma);
     }
 
     {
         auto s = make_segment<soma_segment>(1.0, point<double>(0., 1., 2.));
-
-        EXPECT_EQ(s->volume(), pi<double>()*4./3.);
-        EXPECT_EQ(s->area(),   pi<double>()*4.);
         EXPECT_EQ(s->kind(),   section_kind::soma);
     }
-}
 
-TEST(segments, cable)
-{
-    using namespace arb;
-    using arb::math::pi;
-
-    // take advantage of fact that a cable segment with constant radius 1 and
-    // length 1 has volume=1. and area=2
-    auto length = 1./pi<double>();
-    auto radius = 1.;
+    double length = 1./pi<double>();
+    double radius = 1.;
 
     // single cylindrical frustrum
     {
         auto s = make_segment<cable_segment>(section_kind::dendrite, radius, radius, length);
-
-        EXPECT_EQ(s->volume(), 1.0);
-        EXPECT_EQ(s->area(),   2.0);
         EXPECT_EQ(s->kind(),   section_kind::dendrite);
     }
 
@@ -54,16 +40,8 @@ TEST(segments, cable)
                 std::vector<double>{length, length, length}
             );
 
-        EXPECT_EQ(s->volume(), 3.0);
-        EXPECT_EQ(s->area(),   6.0);
         EXPECT_EQ(s->kind(),   section_kind::axon);
     }
-}
-
-TEST(segments, cable_positions)
-{
-    using namespace arb;
-    using arb::math::pi;
 
     // single frustrum of length 1 and radii 1 and 2
     // the centre of each end are at the origin (0,0,0) and (0,1,0)
@@ -75,8 +53,6 @@ TEST(segments, cable_positions)
                 point<double>(0,0,0), point<double>(0,1,0)
             );
 
-        EXPECT_EQ(s->volume(), math::volume_frustrum(1., 1., 2.));
-        EXPECT_EQ(s->area(),   math::area_frustrum  (1., 1., 2.));
         EXPECT_EQ(s->kind(),   section_kind::dendrite);
     }
 
@@ -90,8 +66,6 @@ TEST(segments, cable_positions)
                 std::vector<point<double>>{ {0,0,0}, {0,0.5,0}, {0,1,0} }
             );
 
-        EXPECT_EQ(s->volume(), math::volume_frustrum(1., 1., 2.));
-        EXPECT_EQ(s->area(),   math::area_frustrum(1., 1., 2.));
         EXPECT_EQ(s->kind(),   section_kind::axon);
     }
 }
diff --git a/test/unit/test_simd.cpp b/test/unit/test_simd.cpp
index dcb102a1..c266b2b5 100644
--- a/test/unit/test_simd.cpp
+++ b/test/unit/test_simd.cpp
@@ -5,8 +5,8 @@
 #include <random>
 #include <unordered_set>
 
-#include <simd/simd.hpp>
-#include <simd/avx.hpp>
+#include <arbor/simd/simd.hpp>
+#include <arbor/simd/avx.hpp>
 
 #include "common.hpp"
 
diff --git a/test/unit/test_spike_source.cpp b/test/unit/test_spike_source.cpp
index 28a327ab..b877a64a 100644
--- a/test/unit/test_spike_source.cpp
+++ b/test/unit/test_spike_source.cpp
@@ -1,9 +1,10 @@
 #include "../gtest.h"
 
-#include <spike_source_cell.hpp>
-#include <spike_source_cell_group.hpp>
-#include <time_sequence.hpp>
-#include <util/unique_any.hpp>
+#include <arbor/spike_source_cell.hpp>
+#include <arbor/time_sequence.hpp>
+#include <arbor/util/unique_any.hpp>
+
+#include "spike_source_cell_group.hpp"
 
 #include "../simple_recipes.hpp"
 
diff --git a/test/unit/test_swcio.cpp b/test/unit/test_swcio.cpp
index c27318b7..85ad98d6 100644
--- a/test/unit/test_swcio.cpp
+++ b/test/unit/test_swcio.cpp
@@ -7,9 +7,11 @@
 #include <type_traits>
 #include <vector>
 
+#include <arbor/mc_cell.hpp>
+#include <arbor/morphology.hpp>
+
 #include "../gtest.h"
 
-#include "cell.hpp"
 #include "swcio.hpp"
 
 // Path to data directory can be overriden at compile time.
@@ -469,7 +471,7 @@ TEST(swc_io, cell_construction) {
     // swc -> morphology
     auto morph = io::swc_as_morphology(io::parse_swc_file(is));
 
-    cell cell = make_cell(morph, true);
+    mc_cell cell = make_mc_cell(morph, true);
     EXPECT_TRUE(cell.has_soma());
     EXPECT_EQ(4u, cell.num_segments());
 
@@ -518,11 +520,38 @@ TEST(swc_io, cell_construction) {
               cell.cable(3)->radius(0));
 }
 
+void expect_morph_eq(const morphology& a, const morphology& b) {
+    EXPECT_EQ(a.soma.r, b.soma.r);
+    EXPECT_EQ(a.sections.size(), b.sections.size());
+
+    for (unsigned i = 0; i<a.sections.size(); ++i) {
+        const section_geometry& r = a.sections[i];
+        const section_geometry& s = b.sections[i];
+
+        EXPECT_EQ(r.points.size(), s.points.size());
+        unsigned n = r.points.size();
+        if (n>1) {
+            auto r0 = r.points[0];
+            auto s0 = s.points[0];
+            EXPECT_EQ(r0.r, s0.r);
+
+            // TODO: check lengths for each line segment instead.
+            EXPECT_DOUBLE_EQ(r.length, s.length);
+
+            for (unsigned i = 1; i<n; ++i) {
+                auto r1 = r.points[i];
+                auto s1 = s.points[i];
+
+                EXPECT_EQ(r1.r, s1.r);
+            }
+        }
+    }
+}
+
 // check that simple ball and stick model with one dendrite attached to a soma
 // which is used in the validation tests can be loaded from file and matches
 // the one generated with the C++ interface
-TEST(swc_parser, from_file_ball_and_stick)
-{
+TEST(swc_parser, from_file_ball_and_stick) {
     std::string datadir{DATADIR};
     auto fname = datadir + "/ball_and_stick.swc";
     std::ifstream fid(fname);
@@ -531,17 +560,14 @@ TEST(swc_parser, from_file_ball_and_stick)
         return;
     }
 
-    // read the file into a cell object
-    auto bas_cell = make_cell(io::swc_as_morphology(io::parse_swc_file(fid)));
+    // read the file as morhpology
+    auto bas_morph = io::swc_as_morphology(io::parse_swc_file(fid));
 
-    // verify that the correct number of nodes was read
-    EXPECT_EQ(2u, bas_cell.num_segments());
-    EXPECT_EQ(2u, bas_cell.num_compartments());
+    // compare with expected morphology
+    morphology expected;
 
-    // make an equivalent cell via C++ interface
-    cell local_cell;
-    local_cell.add_soma(6.30785);
-    local_cell.add_cable(0, section_kind::dendrite, 0.5, 0.5, 200);
+    expected.soma = {0., 0., 0.,  6.30785};
+    expected.add_section({{6.30785, 0., 0., 0.5}, {206.30785, 0., 0., 0.5}}, 0u, section_kind::dendrite);
 
-    EXPECT_TRUE(cell_basic_equality(local_cell, bas_cell));
+    expect_morph_eq(expected, bas_morph);
 }
diff --git a/test/unit/test_synapses.cpp b/test/unit/test_synapses.cpp
index 08a14221..0c5464e6 100644
--- a/test/unit/test_synapses.cpp
+++ b/test/unit/test_synapses.cpp
@@ -4,15 +4,15 @@
 #include <tuple>
 #include <vector>
 
-#include <arbor/util/optional.hpp>
+#include <arbor/constants.hpp>
 #include <arbor/mechcat.hpp>
+#include <arbor/util/optional.hpp>
+#include <arbor/mc_cell.hpp>
 
-#include <cell.hpp>
-#include <constants.hpp>
-#include <backends/multicore/fvm.hpp>
-#include <backends/multicore/mechanism.hpp>
-#include <util/maputil.hpp>
-#include <util/range.hpp>
+#include "backends/multicore/fvm.hpp"
+#include "backends/multicore/mechanism.hpp"
+#include "util/maputil.hpp"
+#include "util/range.hpp"
 
 #include "common.hpp"
 
@@ -41,7 +41,7 @@ ACCESS_BIND(value_type* multicore::mechanism::*, vec_i_ptr, &multicore::mechanis
 TEST(synapses, add_to_cell) {
     using namespace arb;
 
-    ::arb::cell cell;
+    mc_cell cell;
 
     // Soma with diameter 12.6157 um and HH channel
     auto soma = cell.add_soma(12.6157/2.0);
diff --git a/test/unit/test_time_seq.cpp b/test/unit/test_time_seq.cpp
index f5031513..dcb87b4d 100644
--- a/test/unit/test_time_seq.cpp
+++ b/test/unit/test_time_seq.cpp
@@ -3,8 +3,10 @@
 
 #include <vector>
 
-#include <time_sequence.hpp>
-#include <util/rangeutil.hpp>
+#include <arbor/time_sequence.hpp>
+
+#include "event_queue.hpp"
+#include "util/rangeutil.hpp"
 
 using namespace arb;
 using pse = postsynaptic_spike_event;
diff --git a/test/unit/test_unique_any.cpp b/test/unit/test_unique_any.cpp
index 0104619a..038d4341 100644
--- a/test/unit/test_unique_any.cpp
+++ b/test/unit/test_unique_any.cpp
@@ -1,8 +1,9 @@
 #include <iostream>
 
-#include <util/rangeutil.hpp>
-#include <util/span.hpp>
-#include <util/unique_any.hpp>
+#include <arbor/util/unique_any.hpp>
+
+#include "util/rangeutil.hpp"
+#include "util/span.hpp"
 
 #include "../gtest.h"
 #include "common.hpp"
diff --git a/test/validation/convergence_test.hpp b/test/validation/convergence_test.hpp
index 8ee744c2..c4de6033 100644
--- a/test/validation/convergence_test.hpp
+++ b/test/validation/convergence_test.hpp
@@ -4,13 +4,13 @@
 
 #include <nlohmann/json.hpp>
 
-#include <simulation.hpp>
-#include <schedule.hpp>
-#include <sampling.hpp>
-#include <simple_sampler.hpp>
-#include <util/filter.hpp>
-#include <util/rangeutil.hpp>
+#include <arbor/sampling.hpp>
+#include <arbor/simple_sampler.hpp>
 
+#include "simulation.hpp"
+#include "schedule.hpp"
+#include "util/filter.hpp"
+#include "util/rangeutil.hpp"
 
 #include "../gtest.h"
 
@@ -141,7 +141,7 @@ public:
  * Extract time points to exclude from current stimulus end-points.
  */
 
-inline std::vector<float> stimulus_ends(const cell& c) {
+inline std::vector<float> stimulus_ends(const mc_cell& c) {
     std::vector<float> ts;
 
     for (const auto& stimulus: c.stimuli()) {
diff --git a/test/validation/trace_analysis.cpp b/test/validation/trace_analysis.cpp
index 5e0268c3..6a9cd813 100644
--- a/test/validation/trace_analysis.cpp
+++ b/test/validation/trace_analysis.cpp
@@ -7,11 +7,11 @@
 #include "../gtest.h"
 
 #include <arbor/util/optional.hpp>
+#include <arbor/simple_sampler.hpp>
 
-#include <math.hpp>
-#include <simple_sampler.hpp>
-#include <util/partition.hpp>
-#include <util/rangeutil.hpp>
+#include "math.hpp"
+#include "util/partition.hpp"
+#include "util/rangeutil.hpp"
 
 #include "trace_analysis.hpp"
 
diff --git a/test/validation/trace_analysis.hpp b/test/validation/trace_analysis.hpp
index 26f2823b..167952cb 100644
--- a/test/validation/trace_analysis.hpp
+++ b/test/validation/trace_analysis.hpp
@@ -4,17 +4,41 @@
 
 #include "../gtest.h"
 
+#include <arbor/simple_sampler.hpp>
 #include <arbor/util/optional.hpp>
 
-#include <simple_sampler.hpp>
-#include <math.hpp>
-#include <util/path.hpp>
-#include <util/rangeutil.hpp>
+#include "math.hpp"
+#include "util/path.hpp"
+#include "util/deduce_return.hpp"
+#include "util/rangeutil.hpp"
 
 namespace arb {
 
 /* Trace data comparison */
 
+// Extract time or value data from trace.
+
+namespace impl {
+    // NB: work-around for lack of function return type deduction
+    // in C++11; can't use lambda within DEDUCED_RETURN_TYPE.
+
+    template <typename V>
+    inline float time(const trace_entry<V>& x) { return x.t; }
+
+    template <typename V>
+    inline const V& value(const trace_entry<V>& x) { return x.v; }
+}
+
+template <typename V>
+inline auto times(const trace_data<V>& trace) DEDUCED_RETURN_TYPE(
+   util::transform_view(trace, impl::time<V>)
+)
+
+template <typename V>
+inline auto values(const trace_data<V>& trace) DEDUCED_RETURN_TYPE(
+   util::transform_view(trace, impl::value<V>)
+)
+
 // Compute max |v_i - f(t_i)| where (t, v) is the 
 // first trace `u` and f is the piece-wise linear interpolant
 // of the second trace `r`.
diff --git a/test/validation/validate_ball_and_stick.cpp b/test/validation/validate_ball_and_stick.cpp
index e2b6a39d..2520321b 100644
--- a/test/validation/validate_ball_and_stick.cpp
+++ b/test/validation/validate_ball_and_stick.cpp
@@ -1,18 +1,18 @@
 #include <iostream>
 
-#include <arbor/common_types.hpp>
 #include <nlohmann/json.hpp>
 
-#include <cell.hpp>
-#include <load_balance.hpp>
-#include <hardware/node_info.hpp>
-#include <hardware/gpu.hpp>
-#include <simulation.hpp>
-#include <recipe.hpp>
-#include <segment.hpp>
-#include <simple_sampler.hpp>
-#include <util/meta.hpp>
-#include <util/path.hpp>
+#include <arbor/common_types.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/simple_sampler.hpp>
+
+#include "load_balance.hpp"
+#include "hardware/node_info.hpp"
+#include "hardware/gpu.hpp"
+#include "simulation.hpp"
+#include "recipe.hpp"
+#include "util/meta.hpp"
+#include "util/path.hpp"
 
 #include "../common_cells.hpp"
 #include "../simple_recipes.hpp"
@@ -34,7 +34,7 @@ void run_ncomp_convergence_test(
     const char* model_name,
     const util::path& ref_data_path,
     backend_kind backend,
-    const cell& c,
+    const mc_cell& c,
     ProbePointSeq& probe_points,
     float t_end=100.f)
 {
@@ -90,7 +90,7 @@ void run_ncomp_convergence_test(
 void validate_ball_and_stick(arb::backend_kind backend) {
     using namespace arb;
 
-    cell c = make_cell_ball_and_stick();
+    mc_cell c = make_cell_ball_and_stick();
     probe_point points[] = {
         {"soma.mid", {0u, 0.5}},
         {"dend.mid", {1u, 0.5}},
@@ -108,7 +108,7 @@ void validate_ball_and_stick(arb::backend_kind backend) {
 void validate_ball_and_taper(arb::backend_kind backend) {
     using namespace arb;
 
-    cell c = make_cell_ball_and_taper();
+    mc_cell c = make_cell_ball_and_taper();
     probe_point points[] = {
         {"soma.mid",  {0u, 0.5}},
         {"taper.mid", {1u, 0.5}},
@@ -126,7 +126,7 @@ void validate_ball_and_taper(arb::backend_kind backend) {
 void validate_ball_and_3stick(arb::backend_kind backend) {
     using namespace arb;
 
-    cell c = make_cell_ball_and_3stick();
+    mc_cell c = make_cell_ball_and_3stick();
     probe_point points[] = {
         {"soma.mid",  {0u, 0.5}},
         {"dend1.mid", {1u, 0.5}},
@@ -148,7 +148,7 @@ void validate_ball_and_3stick(arb::backend_kind backend) {
 void validate_rallpack1(arb::backend_kind backend) {
     using namespace arb;
 
-    cell c = make_cell_simple_cable();
+    mc_cell c = make_cell_simple_cable();
     probe_point points[] = {
         {"cable.x0.0", {1u, 0.0}},
         {"cable.x0.3", {1u, 0.3}},
@@ -167,7 +167,7 @@ void validate_rallpack1(arb::backend_kind backend) {
 void validate_ball_and_squiggle(arb::backend_kind backend) {
     using namespace arb;
 
-    cell c = make_cell_ball_and_squiggle();
+    mc_cell c = make_cell_ball_and_squiggle();
     probe_point points[] = {
         {"soma.mid", {0u, 0.5}},
         {"dend.mid", {1u, 0.5}},
diff --git a/test/validation/validate_compartment_policy.cpp b/test/validation/validate_compartment_policy.cpp
index d0543384..a2ead6c4 100644
--- a/test/validation/validate_compartment_policy.cpp
+++ b/test/validation/validate_compartment_policy.cpp
@@ -4,11 +4,11 @@
 #include <nlohmann/json.hpp>
 
 #include <arbor/common_types.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/simple_sampler.hpp>
 
-#include <cell.hpp>
 #include <simulation.hpp>
 #include <recipe.hpp>
-#include <simple_sampler.hpp>
 #include <util/rangeutil.hpp>
 
 #include "../gtest.h"
diff --git a/test/validation/validate_kinetic.cpp b/test/validation/validate_kinetic.cpp
index 54fc750b..063df632 100644
--- a/test/validation/validate_kinetic.cpp
+++ b/test/validation/validate_kinetic.cpp
@@ -1,16 +1,18 @@
 #include "../gtest.h"
 
+#include <string>
+
 #include <nlohmann/json.hpp>
 
 #include <arbor/common_types.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/simple_sampler.hpp>
 
-#include <cell.hpp>
 #include <hardware/node_info.hpp>
 #include <hardware/gpu.hpp>
 #include <load_balance.hpp>
 #include <simulation.hpp>
 #include <recipe.hpp>
-#include <simple_sampler.hpp>
 #include <util/rangeutil.hpp>
 
 #include "../common_cells.hpp"
@@ -22,7 +24,7 @@
 
 void run_kinetic_dt(
     arb::backend_kind backend,
-    arb::cell& c,
+    arb::mc_cell& c,
     arb::cell_probe_address probe,
     float t_end,
     nlohmann::json meta,
@@ -71,7 +73,7 @@ void validate_kinetic_kin1(arb::backend_kind backend) {
     using namespace arb;
 
     // 20 µm diameter soma with single mechanism, current probe
-    cell c;
+    mc_cell c;
     auto soma = c.add_soma(10);
     soma->add_mechanism("test_kin1");
     cell_probe_address probe{{0, 0.5}, cell_probe_address::membrane_current};
@@ -89,7 +91,7 @@ void validate_kinetic_kinlva(arb::backend_kind backend) {
     using namespace arb;
 
     // 20 µm diameter soma with single mechanism, current probe
-    cell c;
+    mc_cell c;
     auto soma = c.add_soma(10);
     c.add_stimulus({0,0.5}, {20., 130., -0.025});
     soma->add_mechanism("test_kinlva");
diff --git a/test/validation/validate_soma.cpp b/test/validation/validate_soma.cpp
index 2beb50bc..e912c6b5 100644
--- a/test/validation/validate_soma.cpp
+++ b/test/validation/validate_soma.cpp
@@ -1,14 +1,14 @@
 #include <nlohmann/json.hpp>
 
 #include <arbor/common_types.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/simple_sampler.hpp>
 
-#include <cell.hpp>
 #include <hardware/gpu.hpp>
 #include <hardware/node_info.hpp>
 #include <load_balance.hpp>
 #include <simulation.hpp>
 #include <recipe.hpp>
-#include <simple_sampler.hpp>
 #include <util/rangeutil.hpp>
 
 #include "../common_cells.hpp"
@@ -25,7 +25,7 @@ using namespace arb;
 void validate_soma(backend_kind backend) {
     float sample_dt = g_trace_io.sample_dt();
 
-    cell c = make_cell_soma_only();
+    mc_cell c = make_cell_soma_only();
 
     cable1d_recipe rec{c};
     rec.add_probe(0, 0, cell_probe_address{{0, 0.5}, cell_probe_address::membrane_voltage});
diff --git a/test/validation/validate_synapses.cpp b/test/validation/validate_synapses.cpp
index 2d0aeab8..01c237d8 100644
--- a/test/validation/validate_synapses.cpp
+++ b/test/validation/validate_synapses.cpp
@@ -1,14 +1,14 @@
 #include <nlohmann/json.hpp>
 
-#include <cell.hpp>
-#include <cell_group.hpp>
-#include <hardware/node_info.hpp>
-#include <hardware/gpu.hpp>
-#include <load_balance.hpp>
-#include <simulation.hpp>
-#include <recipe.hpp>
-#include <simple_sampler.hpp>
-#include <util/path.hpp>
+#include <arbor/mc_cell.hpp>
+#include <arbor/simple_sampler.hpp>
+
+#include "hardware/node_info.hpp"
+#include "hardware/gpu.hpp"
+#include "load_balance.hpp"
+#include "simulation.hpp"
+#include "recipe.hpp"
+#include "util/path.hpp"
 
 #include "../gtest.h"
 
@@ -37,7 +37,7 @@ void run_synapse_test(
         {"backend_kind", to_string(backend)}
     };
 
-    cell c = make_cell_ball_and_stick(false); // no stimuli
+    mc_cell c = make_cell_ball_and_stick(false); // no stimuli
     mechanism_desc syn_default(syn_type);
     c.add_synapse({1, 0.5}, syn_default);
 
diff --git a/test/validation/validation_data.cpp b/test/validation/validation_data.cpp
index 5426a57b..2722c909 100644
--- a/test/validation/validation_data.cpp
+++ b/test/validation/validation_data.cpp
@@ -6,9 +6,11 @@
 
 #include <nlohmann/json.hpp>
 
-#include <simple_sampler.hpp>
-#include <util/path.hpp>
+#include <arbor/simple_sampler.hpp>
 
+#include "util/path.hpp"
+
+#include "trace_analysis.hpp"
 #include "validation_data.hpp"
 
 namespace arb {
diff --git a/test/validation/validation_data.hpp b/test/validation/validation_data.hpp
index 84056ebb..1317738f 100644
--- a/test/validation/validation_data.hpp
+++ b/test/validation/validation_data.hpp
@@ -7,8 +7,9 @@
 
 #include <nlohmann/json.hpp>
 
-#include <simple_sampler.hpp>
-#include <util/path.hpp>
+#include <arbor/simple_sampler.hpp>
+
+#include "util/path.hpp"
 
 namespace arb {
 
-- 
GitLab