diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp
index 1a3065ec1c8d3178ccd0a2374a51948d2bc7be65..00adf7e5723a35f0b009e07e43d29ae49d38c4f0 100644
--- a/miniapp/miniapp.cpp
+++ b/miniapp/miniapp.cpp
@@ -26,7 +26,6 @@
 using namespace nest::mc;
 
 using global_policy = communication::global_policy;
-using communicator_type = communication::communicator<global_policy>;
 
 using lowered_cell = fvm::fvm_cell<double, cell_local_size_type>;
 using model_type = model<lowered_cell>;
diff --git a/miniapp/miniapp_recipes.cpp b/miniapp/miniapp_recipes.cpp
index 8de187b9e2ccf14394c66c208c291aaecec9b7ef..0c8b7a2d7c8c3ebd7aa81c78a554ba823e1462f8 100644
--- a/miniapp/miniapp_recipes.cpp
+++ b/miniapp/miniapp_recipes.cpp
@@ -15,7 +15,12 @@ namespace mc {
 // description for greater data reuse.
 
 template <typename RNG>
-mc::cell make_basic_cell(unsigned compartments_per_segment, unsigned num_synapses, const std::string& syn_type, RNG& rng) {
+cell make_basic_cell(
+    unsigned compartments_per_segment,
+    unsigned num_synapses,
+    const std::string& syn_type,
+    RNG& rng)
+{
     nest::mc::cell cell;
 
     // Soma with diameter 12.6157 um and HH channel
diff --git a/miniapp/trace_sampler.hpp b/miniapp/trace_sampler.hpp
index b0ea4dcb01a44294c409bed1ea2cd4dbd7eeafa6..2e10257a12883e2dd9f8c69d2a2894cdbc001ea6 100644
--- a/miniapp/trace_sampler.hpp
+++ b/miniapp/trace_sampler.hpp
@@ -13,10 +13,10 @@ namespace nest {
 namespace mc {
 
 // move sampler code to another source file...
-template <typename TimeT=float, typename ValueT=double>
+template <typename Time=float, typename Value=double>
 struct sample_trace {
-    using time_type = TimeT;
-    using value_type = ValueT;
+    using time_type = Time;
+    using value_type = Value;
 
     struct sample_type {
         time_type time;
@@ -34,10 +34,10 @@ struct sample_trace {
     {}
 };
 
-template <typename TimeT=float, typename ValueT=double>
+template <typename Time=float, typename Value=double>
 struct trace_sampler {
-    using time_type = TimeT;
-    using value_type = ValueT;
+    using time_type = Time;
+    using value_type = Value;
 
     float next_sample_t() const { return t_next_sample_; }
 
@@ -62,9 +62,9 @@ private:
 };
 
 // with type deduction ...
-template <typename TimeT, typename ValueT>
-trace_sampler<TimeT, ValueT> make_trace_sampler(sample_trace<TimeT, ValueT> *trace, TimeT sample_dt, TimeT tfrom=0) {
-    return trace_sampler<TimeT, ValueT>(trace, sample_dt, tfrom);
+template <typename Time, typename Value>
+trace_sampler<Time, Value> make_trace_sampler(sample_trace<Time, Value> *trace, Time sample_dt, Time tfrom=0) {
+    return trace_sampler<Time, Value>(trace, sample_dt, tfrom);
 }
 
 } // namespace mc
diff --git a/src/catypes.hpp b/src/catypes.hpp
index cf12b768359fd8e673eb77d33c822375c9c09e74..0337c0ac2831f1fde39b56f86d71fa7b6d36129c 100644
--- a/src/catypes.hpp
+++ b/src/catypes.hpp
@@ -20,29 +20,18 @@ using cell_gid_type = std::uint32_t;
 using cell_size_type = typename std::make_unsigned<cell_gid_type>::type;
 
 // for indexes into cell-local data
-using cell_local_index_type = std::uint32_t;
+using cell_lid_type = std::uint32_t;
 
 // for counts of cell-local data
-using cell_local_size_type = typename std::make_unsigned<cell_local_index_type>::type;
+using cell_local_size_type = typename std::make_unsigned<cell_lid_type>::type;
 
 struct cell_member_type {
     cell_gid_type gid;
-    cell_local_index_type index;
+    cell_lid_type index;
 };
 
 DEFINE_LEXICOGRAPHIC_ORDERING(cell_member_type,(a.gid,a.index),(b.gid,b.index))
 
-// good idea? bad idea?
-using packed_cell_member_type = std::uint64_t;
-
-inline packed_cell_member_type pack_cell_member(cell_member_type m) {
-    return ((packed_cell_member_type)m.gid<<32u) + m.index;
-}
-
-inline cell_member_type unpack_cell_member(packed_cell_member_type p) {
-    return {(cell_gid_type)(p>>32u), (cell_local_index_type)(p&((1ull<<32)-1))};
-}
-
 } // namespace mc
 } // namespace nest
 
diff --git a/src/cell.cpp b/src/cell.cpp
index 73e1960b421394336161db7671ec41ff51d18b66..cf4dd478367c318c6a3405eeb61f437fc0b68f46 100644
--- a/src/cell.cpp
+++ b/src/cell.cpp
@@ -77,7 +77,7 @@ cable_segment* cell::add_cable(cell::index_type parent, segment_ptr&& cable)
 
 segment* cell::segment(index_type index)
 {
-    if(index<0 || index>=num_segments()) {
+    if (index>=num_segments()) {
         throw std::out_of_range(
             "attempt to access a segment with invalid index"
         );
@@ -87,7 +87,7 @@ segment* cell::segment(index_type index)
 
 segment const* cell::segment(index_type index) const
 {
-    if(index<0 || index>=num_segments()) {
+    if (index>=num_segments()) {
         throw std::out_of_range(
             "attempt to access a segment with invalid index"
         );
diff --git a/src/cell.hpp b/src/cell.hpp
index 5d01241c5d3d002daff5662d8cd68db6da650bd1..effdf9254a22b1a5db20673544274a0784db9fce 100644
--- a/src/cell.hpp
+++ b/src/cell.hpp
@@ -23,7 +23,7 @@ struct compartment_model {
 };
 
 struct segment_location {
-    segment_location(cell_local_index_type s, double l)
+    segment_location(cell_lid_type s, double l)
     : segment(s), position(l)
     {
         EXPECTS(position>=0. && position<=1.);
@@ -31,7 +31,7 @@ struct segment_location {
     friend bool operator==(segment_location l, segment_location r) {
         return l.segment==r.segment && l.position==r.position;
     }
-    cell_local_index_type segment;
+    cell_lid_type segment;
     double position;
 };
 
@@ -53,9 +53,7 @@ struct probe_spec {
 /// high-level abstract representation of a cell and its segments
 class cell {
 public:
-
-    // types
-    using index_type = cell_local_index_type;
+    using index_type = cell_lid_type;
     using size_type = cell_local_size_type;
     using value_type = double;
     using point_type = point<value_type>;
diff --git a/src/cell_group.hpp b/src/cell_group.hpp
index f667ebdbeabb7ed93f0941bfba076eda45f94296..69a94a98fa3de23890b735a3d52a1c381c70322d 100644
--- a/src/cell_group.hpp
+++ b/src/cell_group.hpp
@@ -119,7 +119,7 @@ public:
         }
     }
 
-    const std::vector<spike<source_id_type>>&
+    const std::vector<spike<source_id_type, time_type>>&
     spikes() const { return spikes_; }
 
     cell_type&       cell()       { return cell_; }
@@ -161,13 +161,13 @@ private:
     std::vector<spike_source_type> spike_sources_;
 
     //. spikes that are generated
-    std::vector<spike<source_id_type>> spikes_;
+    std::vector<spike<source_id_type, time_type>> spikes_;
 
     /// pending events to be delivered
-    event_queue<postsynaptic_spike_event> events_;
+    event_queue<postsynaptic_spike_event<time_type>> events_;
 
     /// pending samples to be taken
-    event_queue<sample_event> sample_events_;
+    event_queue<sample_event<time_type>> sample_events_;
 
     /// the global id of the first target (e.g. a synapse) in this group
     index_type first_target_gid_;
diff --git a/src/cell_tree.hpp b/src/cell_tree.hpp
index 4a5ccf57379b54129f5f0310a74854ecbf025b60..3bbcb3ef4816ea61dcd8702b1587069f342645be 100644
--- a/src/cell_tree.hpp
+++ b/src/cell_tree.hpp
@@ -33,7 +33,7 @@ class cell_tree {
     using range = memory::Range;
 
 public:
-    using int_type        = cell_local_index_type;
+    using int_type        = cell_lid_type;
     using size_type       = cell_local_size_type;
 
     using index_type      = memory::HostVector<int_type>;
diff --git a/src/communication/communicator.hpp b/src/communication/communicator.hpp
index 79ea52a2047bac4b4c53e98aa8b9bfee795cdc8a..ba6cd612e3a96a39648d3ac6f8093b5859212db1 100644
--- a/src/communication/communicator.hpp
+++ b/src/communication/communicator.hpp
@@ -41,12 +41,14 @@ namespace communication {
 // For each double buffer, the current buffer (accessed via buffer.get())
 // is exposed to the user, and the other buffer is used inside exchange().
 
-template <typename CommunicationPolicy>
+template <typename Time, typename CommunicationPolicy>
 class communicator {
 private:
     using communication_policy_type = CommunicationPolicy;
     using id_type = cell_gid_type;
-    using spike_type = spike<cell_member_type>;
+    using time_type = Time;
+    using spike_type = spike<cell_member_type, time_type>;
+    using connection_type = connection<time_type>;
 
     /// thread private storage for accumulating spikes
     using local_spike_store_type =
@@ -54,7 +56,7 @@ private:
 
     /// per-cell group lists of events to be delivered
     using event_queue =
-        std::vector<postsynaptic_spike_event>;
+        std::vector<postsynaptic_spike_event<time_type>>;
 
     /// double buffered storage of the thread private spike lists
     util::double_buffer<local_spike_store_type> thread_spikes_;
@@ -74,12 +76,19 @@ private:
         return thread_spikes_.get().local();
     }
 
-    void clear_buffered_spikes() {
-        for (auto& v : buffered_spikes()) {
-            v.clear();
+    template <typename Container>
+    void clear(Container& c) {
+        for(auto& item : c) {
+            item.clear();
         }
     }
 
+    void clear_spikes()          { clear(thread_spikes_.get()); }
+    void clear_buffered_spikes() { clear(buffered_spikes()); }
+
+    void clear_events()          { clear(events_.get()); }
+    void clear_buffered_events() { clear(events_.other()); }
+
     std::vector<spike_type> gather_local_spikes() {
         std::vector<spike_type> spikes;
         for (auto& v : buffered_spikes()) {
@@ -88,7 +97,13 @@ private:
         return spikes;
     }
 
-    std::vector<connection> connections_;
+    std::size_t cell_group_index(cell_gid_type cell_gid) const {
+        // this will be more elaborate when there is more than one cell per cell group
+        EXPECTS(cell_gid>=cell_gid_from_ && cell_gid<cell_gid_to_);
+        return cell_gid-cell_gid_from_;
+    }
+
+    std::vector<connection_type> connections_;
 
     communication_policy_type communication_policy_;
 
@@ -112,7 +127,7 @@ public:
         events_.other().resize(num_groups_local_);
     }
 
-    void add_connection(connection con) {
+    void add_connection(connection_type con) {
         EXPECTS(is_local_cell(con.destination().gid));
         connections_.push_back(con);
     }
@@ -128,8 +143,8 @@ public:
         }
     }
 
-    float min_delay() {
-        auto local_min = std::numeric_limits<float>::max();
+    time_type min_delay() {
+        auto local_min = std::numeric_limits<time_type>::max();
         for (auto& con : connections_) {
             local_min = std::min(local_min, con.delay());
         }
@@ -153,14 +168,13 @@ public:
         num_spikes_ += global_spikes.size();
         clear_buffered_spikes();
 
-        // clear the event queue buffers, which will hold the events generated by the
-        // global_spikes in the exchange
-        auto& queues = events_.other();
-        for (auto& q : queues) {
-            q.clear();
-        }
+        // clear the event queue buffers, which will store the events generated from the
+        // global_spikes below
+        clear_buffered_events();
 
-        // check all global spikes to see if they will generate local events
+        // Check each global spike in turn to see it generates local events.
+        // If so, make the events and insert them into the appropriate event list
+        auto& queues = events_.other();
         for (auto spike : global_spikes) {
             // search for targets
             auto targets =
@@ -170,7 +184,7 @@ public:
 
             // generate an event for each target
             for (auto it=targets.first; it!=targets.second; ++it) {
-                auto gidx = it->destination().gid - cell_gid_from_;
+                auto gidx = cell_group_index(it->destination().gid);
                 queues[gidx].push_back(it->make_event(spike));
             }
         }
@@ -178,11 +192,11 @@ public:
 
     uint64_t num_spikes() const { return num_spikes_; }
 
-    const std::vector<postsynaptic_spike_event>& queue(int i) const {
+    const std::vector<postsynaptic_spike_event<time_type>>& queue(int i) const {
         return events_.get()[i];
     }
 
-    const std::vector<connection>& connections() const {
+    const std::vector<connection_type>& connections() const {
         return connections_;
     }
 
@@ -195,6 +209,12 @@ public:
         events_.exchange();
     }
 
+    void reset() {
+        clear_buffered_events();
+        clear_buffered_spikes();
+        clear_events();
+        clear_spikes();
+    }
 };
 
 } // namespace communication
diff --git a/src/communication/mpi_global_policy.hpp b/src/communication/mpi_global_policy.hpp
index 8ff8e8830dae040ff80cb0a04d77ef8b43d0f67e..54ded32cc059e2bff221a880edbd3ee68c4f272b 100644
--- a/src/communication/mpi_global_policy.hpp
+++ b/src/communication/mpi_global_policy.hpp
@@ -18,10 +18,9 @@ namespace mc {
 namespace communication {
 
 struct mpi_global_policy {
-    using id_type = cell_member_type;
-
-    std::vector<spike<id_type>> 
-    static gather_spikes(const std::vector<spike<id_type>>& local_spikes) {
+    template <typename I, typename T>
+    static std::vector<spike<I, T>>
+    gather_spikes(const std::vector<spike<I,T>>& local_spikes) {
         return mpi::gather_all(local_spikes);
     }
 
diff --git a/src/communication/serial_global_policy.hpp b/src/communication/serial_global_policy.hpp
index d18e91a6550ed3324d5c191e163c790cce77db9d..9af5eae3fd0dd84be71cb6346018c607fa9ab3df 100644
--- a/src/communication/serial_global_policy.hpp
+++ b/src/communication/serial_global_policy.hpp
@@ -4,7 +4,6 @@
 #include <type_traits>
 #include <vector>
 
-#include <catypes.hpp>
 #include <spike.hpp>
 
 namespace nest {
@@ -12,10 +11,9 @@ namespace mc {
 namespace communication {
 
 struct serial_global_policy {
-    using id_type = cell_member_type;
-
-    std::vector<spike<id_type>> const
-    static gather_spikes(const std::vector<spike<id_type>>& local_spikes) {
+    template <typename I, typename T>
+    static const std::vector<spike<I, T>>&
+    gather_spikes(const std::vector<spike<I, T>>& local_spikes) {
         return local_spikes;
     }
 
diff --git a/src/connection.hpp b/src/connection.hpp
index b08bbc88d3c3046f8da3e2185a7b9f4b631d2482..3ef99a9c28e1007c88835b9e1df1be4765bc32f5 100644
--- a/src/connection.hpp
+++ b/src/connection.hpp
@@ -9,11 +9,13 @@
 namespace nest {
 namespace mc {
 
+template <typename Time>
 class connection {
 public:
     using id_type = cell_member_type;
+    using time_type = Time;
 
-    connection(id_type src, id_type dest, float w, float d) :
+    connection(id_type src, id_type dest, float w, time_type d) :
         source_(src),
         destination_(dest),
         weight_(w),
@@ -26,7 +28,7 @@ public:
     id_type source() const { return source_; }
     id_type destination() const { return destination_; }
 
-    postsynaptic_spike_event make_event(spike<id_type> s) {
+    postsynaptic_spike_event<time_type> make_event(spike<id_type, time_type> s) {
         return {destination_, s.time + delay_, weight_};
     }
 
@@ -34,32 +36,32 @@ private:
     id_type source_;
     id_type destination_;
     float weight_;
-    float delay_;
+    time_type delay_;
 };
 
 // connections are sorted by source id
 // these operators make for easy interopability with STL algorithms
 
-static inline
-bool operator< (connection lhs, connection rhs) {
+template <typename T>
+static inline bool operator<(connection<T> lhs, connection<T> rhs) {
     return lhs.source() < rhs.source();
 }
 
-static inline
-bool operator< (connection lhs, connection::id_type rhs) {
+template <typename T>
+static inline bool operator<(connection<T> lhs, typename connection<T>::id_type rhs) {
     return lhs.source() < rhs;
 }
 
-static inline
-bool operator< (connection::id_type lhs, connection rhs) {
+template <typename T>
+static inline bool operator<(typename connection<T>::id_type lhs, connection<T> rhs) {
     return lhs < rhs.source();
 }
 
 } // namespace mc
 } // namespace nest
 
-static inline
-std::ostream& operator<<(std::ostream& o, nest::mc::connection const& con) {
+template <typename T>
+static inline std::ostream& operator<<(std::ostream& o, nest::mc::connection<T> const& con) {
     return o << "con [" << con.source() << " -> " << con.destination()
              << " : weight " << con.weight()
              << ", delay " << con.delay() << "]";
diff --git a/src/event_queue.hpp b/src/event_queue.hpp
index 8d04f9db0a1ecddaf24680c2e75ce00b56a31590..b78cc0ca821aa2d393c64280164adad50f355e74 100644
--- a/src/event_queue.hpp
+++ b/src/event_queue.hpp
@@ -10,20 +10,33 @@
 namespace nest {
 namespace mc {
 
+/* An event class Event must comply with the following conventions:
+ * Typedefs:
+ *     time_type               floating point type used to represent event times
+ * Member functions:
+ *     time_type when() const  return time value associated with event
+ */
+
+template <typename Time>
 struct postsynaptic_spike_event {
+    using time_type = Time;
+
     cell_member_type target;
-    float time;
+    time_type time;
     float weight;
-};
 
-inline float event_time(const postsynaptic_spike_event &ev) { return ev.time; }
+    time_type when() const { return time; }
+};
 
+template <typename Time>
 struct sample_event {
+    using time_type = Time;
+
     std::uint32_t sampler_index;
-    float time;
-};
+    time_type time;
 
-inline float event_time(const sample_event &ev) { return ev.time; }
+    time_type when() const { return time; }
+};
 
 /* Event objects must have a method event_time() which returns a value
  * from a type with a total ordering with respect to <, >, etc.
@@ -33,7 +46,7 @@ template <typename Event>
 class event_queue {
 public :
     using value_type = Event;
-    using time_type = decltype(event_time(std::declval<Event>()));
+    using time_type = typename Event::time_type;
 
     // create
     event_queue() {}
@@ -47,7 +60,7 @@ public :
     }
 
     // push thing
-    void push(const value_type &e) {
+    void push(const value_type& e) {
          queue_.push(e);
     }
 
@@ -57,7 +70,7 @@ public :
 
     // pop until
     util::optional<value_type> pop_if_before(time_type t_until) {
-         if (!queue_.empty() && event_time(queue_.top()) < t_until) {
+         if (!queue_.empty() && queue_.top().when() < t_until) {
              auto ev = queue_.top();
              queue_.pop();
              return ev;
@@ -74,8 +87,8 @@ public :
 
 private:
     struct event_greater {
-        bool operator()(const Event &a, const Event &b) {
-            return event_time(a) > event_time(b);
+        bool operator()(const Event& a, const Event& b) {
+            return a.when() > b.when();
         }
     };
 
@@ -89,8 +102,8 @@ private:
 } // namespace nest
 } // namespace mc
 
-inline
-std::ostream& operator<< (std::ostream& o, const nest::mc::postsynaptic_spike_event& e)
+template <typename T>
+inline std::ostream& operator<<(std::ostream& o, const nest::mc::postsynaptic_spike_event<T>& e)
 {
     return o << "event[" << e.target << "," << e.time << "," << e.weight << "]";
 }
diff --git a/src/fvm_cell.hpp b/src/fvm_cell.hpp
index a613fbee410e447d5947742551486f533546a28c..1717638931be43fde399db56ec1a7210e39616a3 100644
--- a/src/fvm_cell.hpp
+++ b/src/fvm_cell.hpp
@@ -113,7 +113,8 @@ public:
     void advance(value_type dt);
 
     /// pass an event to the appropriate synapse and call net_receive
-    void apply_event(postsynaptic_spike_event e) {
+    template <typename Time>
+    void apply_event(postsynaptic_spike_event<Time> e) {
         mechanisms_[synapse_index_]->net_receive(e.target.index, e.weight);
     }
 
@@ -343,7 +344,7 @@ fvm_cell<T, I>::fvm_cell(nest::mc::cell const& cell)
 
     synapse_index_ = mechanisms_.size();
 
-    std::map<std::string, std::vector<cell_local_index_type>> syn_map;
+    std::map<std::string, std::vector<cell_lid_type>> syn_map;
     for (const auto& syn : cell.synapses()) {
         syn_map[syn.mechanism.name()].push_back(find_compartment_index(syn.location, graph));
     }
@@ -373,7 +374,7 @@ fvm_cell<T, I>::fvm_cell(nest::mc::cell const& cell)
                 }
             }
         }
-        std::vector<cell_local_index_type> indexes(index_set.begin(), index_set.end());
+        std::vector<cell_lid_type> indexes(index_set.begin(), index_set.end());
 
         // create the ion state
         if(indexes.size()) {
diff --git a/src/model.hpp b/src/model.hpp
index 2e4a2c60e233410a27a7598de19709d2818259f9..44d0b1149791c32dac15fa9e241df4e80912701f 100644
--- a/src/model.hpp
+++ b/src/model.hpp
@@ -16,11 +16,12 @@ namespace nest {
 namespace mc {
 
 template <typename Cell>
-struct model {
+class model {
+public:
     using cell_group_type = cell_group<Cell>;
     using time_type = typename cell_group_type::time_type;
     using value_type = typename cell_group_type::value_type;
-    using communicator_type = communication::communicator<communication::global_policy>;
+    using communicator_type = communication::communicator<time_type, communication::global_policy>;
     using sampler_function = typename cell_group_type::sampler_function;
 
     struct probe_record {
@@ -43,7 +44,7 @@ struct model {
                 auto idx = i-cell_from_;
                 cell_groups_[idx] = cell_group_type(i, cell);
 
-                cell_local_index_type j = 0;
+                cell_lid_type j = 0;
                 for (const auto& probe: cell.probes()) {
                     cell_member_type probe_id{i,j++};
                     probes.push_back({probe_id, probe});
@@ -55,20 +56,19 @@ struct model {
 
         for (cell_gid_type i=cell_from_; i<cell_to_; ++i) {
             for (const auto& cc: rec.connections_on(i)) {
-                // currently cell_connection and connection are basically the same data;
-                // merge?
-                communicator_.add_connection(connection{cc.source, cc.dest, cc.weight, cc.delay});
+                connection<time_type> conn{cc.source, cc.dest, cc.weight, cc.delay};
+                communicator_.add_connection(conn);
             }
         }
         communicator_.construct();
     }
 
-
     void reset() {
         t_ = 0.;
         for (auto& group: cell_groups_) {
             group.reset();
         }
+        communicator_.reset();
     }
 
     time_type run(time_type tfinal, time_type dt) {
@@ -76,29 +76,41 @@ struct model {
         while (t_<tfinal) {
             auto tuntil = std::min(t_+min_delay, tfinal);
 
-            // ensure that spikes are available for exchange
+            // this is crude: the flow of spikes and events should be modelled explicitly
             communicator_.swap_buffers();
 
-            threading::parallel_for::apply(
-                0u, cell_groups_.size(),
-                [&](unsigned i) {
-                    auto &group = cell_groups_[i];
+            tbb::task_group g;
+
+            // should this take a reference to the input event queue
+            // and return a reference to the spikes?
+            auto update_cells = [&] () {
+                threading::parallel_for::apply(
+                    0u, cell_groups_.size(),
+                    [&](unsigned i) {
+                        auto &group = cell_groups_[i];
 
-                    PE("stepping","events");
-                    group.enqueue_events(communicator_.queue(i));
-                    PL();
+                        PE("stepping","events");
+                        group.enqueue_events(communicator_.queue(i));
+                        PL();
 
-                    group.advance(tuntil, dt);
+                        group.advance(tuntil, dt);
 
-                    PE("events");
-                    communicator_.add_spikes(group.spikes());
-                    group.clear_spikes();
-                    PL(2);
-                });
+                        PE("events");
+                        communicator_.add_spikes(group.spikes());
+                        group.clear_spikes();
+                        PL(2);
+                    });
+            };
+
+            auto exchange = [&] () {
+                PE("stepping", "exchange");
+                communicator_.exchange();
+                PL(2);
+            };
 
-            PE("stepping", "exchange");
-            communicator_.exchange();
-            PL(2);
+            g.run(exchange);
+            g.run(update_cells);
+            g.wait();
 
             t_ = tuntil;
         }
diff --git a/src/spike.hpp b/src/spike.hpp
index 2779738987d62daf5d1da834a5667c79b4e57f58..d3ea02551456d2408712886dcff8125b314630e9 100644
--- a/src/spike.hpp
+++ b/src/spike.hpp
@@ -6,15 +6,17 @@
 namespace nest {
 namespace mc {
 
-template <typename I>
+template <typename I, typename Time>
 struct spike {
     using id_type = I;
+    using time_type = Time;
+
     id_type source = id_type{};
-    float time = -1.;
+    time_type time = -1.;
 
     spike() = default;
 
-    spike(id_type s, float t) :
+    spike(id_type s, time_type t) :
         source(s), time(t)
     {}
 };
@@ -23,15 +25,15 @@ struct spike {
 } // namespace nest
 
 /// custom stream operator for printing nest::mc::spike<> values
-template <typename I>
-std::ostream& operator<<(std::ostream& o, nest::mc::spike<I> s) {
+template <typename I, typename T>
+std::ostream& operator<<(std::ostream& o, nest::mc::spike<I, T> s) {
     return o << "spike[t " << s.time << ", src " << s.source << "]";
 }
 
 /// less than comparison operator for nest::mc::spike<> values
 /// spikes are ordered by spike time, for use in sorting and queueing
-template <typename I>
-bool operator<(nest::mc::spike<I> lhs, nest::mc::spike<I> rhs) {
+template <typename I, typename T>
+bool operator<(nest::mc::spike<I, T> lhs, nest::mc::spike<I, T> rhs) {
     return lhs.time < rhs.time;
 }
 
diff --git a/tests/unit/test_compartments.cpp b/tests/unit/test_compartments.cpp
index c109f9cda9a335ce32f695f85ccd0295446a5d92..3ca0acbcabf995637b77c79173431e34e449617f 100644
--- a/tests/unit/test_compartments.cpp
+++ b/tests/unit/test_compartments.cpp
@@ -15,13 +15,13 @@ TEST(compartments, compartment)
 
     {
         nest::mc::compartment c(100, 1.2, 2.1, 2.2);
-        EXPECT_EQ(c.index, 100);
+        EXPECT_EQ(c.index, 100u);
         EXPECT_EQ(c.length, 1.2);
         EXPECT_EQ(left(c.radius), 2.1);
         EXPECT_EQ(right(c.radius), 2.2);
 
         auto c2 = c;
-        EXPECT_EQ(c2.index, 100);
+        EXPECT_EQ(c2.index, 100u);
         EXPECT_EQ(c2.length, 1.2);
         EXPECT_EQ(left(c2.radius), 2.1);
         EXPECT_EQ(right(c2.radius), 2.2);
@@ -29,7 +29,7 @@ TEST(compartments, compartment)
 
     {
         nest::mc::compartment c{100, 1, 2, 3};
-        EXPECT_EQ(c.index, 100);
+        EXPECT_EQ(c.index, 100u);
         EXPECT_EQ(c.length, 1.);
         EXPECT_EQ(left(c.radius), 2.);
         EXPECT_EQ(right(c.radius), 3.);
@@ -54,7 +54,7 @@ TEST(compartments, compartment_iterator)
     ++it;
     {
         auto c = *it;
-        EXPECT_EQ(c.index, 1);
+        EXPECT_EQ(c.index, 1u);
         EXPECT_EQ(left(c.radius), 3.0);
         EXPECT_EQ(right(c.radius), 5.0);
         EXPECT_EQ(c.length, 2.5);
@@ -65,7 +65,7 @@ TEST(compartments, compartment_iterator)
     // returned iterator should be unchanged
     {
         auto c = *(it++);
-        EXPECT_EQ(c.index, 1);
+        EXPECT_EQ(c.index, 1u);
         EXPECT_EQ(left(c.radius), 3.0);
         EXPECT_EQ(right(c.radius), 5.0);
         EXPECT_EQ(c.length, 2.5);
@@ -73,7 +73,7 @@ TEST(compartments, compartment_iterator)
     // while the iterator itself was updated
     {
         auto c = *it;
-        EXPECT_EQ(c.index, 2);
+        EXPECT_EQ(c.index, 2u);
         EXPECT_EQ(left(c.radius), 5.0);
         EXPECT_EQ(right(c.radius), 7.0);
         EXPECT_EQ(c.length, 2.5);
@@ -84,7 +84,7 @@ TEST(compartments, compartment_iterator)
         // copy iterator
         auto it2 = it;
         auto c = *it2;
-        EXPECT_EQ(c.index, 2);
+        EXPECT_EQ(c.index, 2u);
         EXPECT_EQ(left(c.radius), 5.0);
         EXPECT_EQ(right(c.radius), 7.0);
         EXPECT_EQ(c.length, 2.5);
@@ -96,7 +96,7 @@ TEST(compartments, compartment_iterator)
 
         // check the copy has updated correctly when incremented
         c= *it2;
-        EXPECT_EQ(c.index, 3);
+        EXPECT_EQ(c.index, 3u);
         EXPECT_EQ(left(c.radius), 7.0);
         EXPECT_EQ(right(c.radius), 9.0);
         EXPECT_EQ(c.length, 2.5);
@@ -112,7 +112,7 @@ TEST(compartments, compartment_range)
         EXPECT_EQ((*rng.end()).index, 10);
         EXPECT_NE(rng.begin(), rng.end());
 
-        auto count = 0;
+        int count = 0;
         for(auto c : rng) {
             EXPECT_EQ(c.index, count);
             auto er = 1.0 + double(count)/10.;
diff --git a/tests/unit/test_event_queue.cpp b/tests/unit/test_event_queue.cpp
index 558ac1c293f5f53b3a562481d7193b90031c335f..f4e31b26715fa2aa3cb690720442cc68f2b18fe2 100644
--- a/tests/unit/test_event_queue.cpp
+++ b/tests/unit/test_event_queue.cpp
@@ -7,7 +7,7 @@
 TEST(event_queue, push)
 {
     using namespace nest::mc;
-    using ps_event_queue = event_queue<postsynaptic_spike_event>;
+    using ps_event_queue = event_queue<postsynaptic_spike_event<float>>;
 
     ps_event_queue q;
 
@@ -31,9 +31,9 @@ TEST(event_queue, push)
 TEST(event_queue, push_range)
 {
     using namespace nest::mc;
-    using ps_event_queue = event_queue<postsynaptic_spike_event>;
+    using ps_event_queue = event_queue<postsynaptic_spike_event<float>>;
 
-    postsynaptic_spike_event events[] = {
+    postsynaptic_spike_event<float> events[] = {
         {{1u, 0u}, 2.f, 2.f},
         {{4u, 1u}, 1.f, 2.f},
         {{8u, 2u}, 20.f, 2.f},
@@ -56,7 +56,7 @@ TEST(event_queue, push_range)
 TEST(event_queue, pop_if_before)
 {
     using namespace nest::mc;
-    using ps_event_queue = event_queue<postsynaptic_spike_event>;
+    using ps_event_queue = event_queue<postsynaptic_spike_event<float>>;
 
     cell_member_type target[4] = {
         {1u, 0u},
@@ -65,7 +65,7 @@ TEST(event_queue, pop_if_before)
         {2u, 3u}
     };
 
-    postsynaptic_spike_event events[] = {
+    postsynaptic_spike_event<float> events[] = {
         {target[0], 1.f, 2.f},
         {target[1], 2.f, 2.f},
         {target[2], 3.f, 2.f},
diff --git a/tests/unit/test_fvm.cpp b/tests/unit/test_fvm.cpp
index 99e676cb317fe566142cb2c713b0a83834dd2ee6..ea8cd7dae4f1b2cc17af3c39cfd3422169180dca 100644
--- a/tests/unit/test_fvm.cpp
+++ b/tests/unit/test_fvm.cpp
@@ -41,7 +41,7 @@ TEST(fvm, cable)
     cell.segment(1)->set_compartments(4);
     cell.segment(2)->set_compartments(4);
 
-    using fvm_cell = fvm::fvm_cell<double, cell_local_index_type>;
+    using fvm_cell = fvm::fvm_cell<double, cell_lid_type>;
     fvm_cell fvcell(cell);
     auto& J = fvcell.jacobian();
 
@@ -91,7 +91,7 @@ TEST(fvm, init)
 
     cell.segment(1)->set_compartments(10);
 
-    using fvm_cell = fvm::fvm_cell<double, cell_local_index_type>;
+    using fvm_cell = fvm::fvm_cell<double, cell_lid_type>;
     fvm_cell fvcell(cell);
     auto& J = fvcell.jacobian();
     EXPECT_EQ(J.size(), 11u);
diff --git a/tests/validation/validate_synapses.cpp b/tests/validation/validate_synapses.cpp
index 3dbf7fbb62efc44d128f58222b22f3f2ae437eaf..9bc67172879d14e8aea1aed21311c344fcb9f64a 100644
--- a/tests/validation/validate_synapses.cpp
+++ b/tests/validation/validate_synapses.cpp
@@ -75,7 +75,7 @@ void run_neuron_baseline(const char* syn_type, const char* data_file)
     auto probe_dend = cell.add_probe({{1,0.5}, probeKind::membrane_voltage});
 
     // injected spike events
-    postsynaptic_spike_event synthetic_events[] = {
+    postsynaptic_spike_event<float> synthetic_events[] = {
         {{0u, 0u}, 10.0, 0.04},
         {{0u, 0u}, 20.0, 0.04},
         {{0u, 0u}, 40.0, 0.04}