diff --git a/.gitignore b/.gitignore
index 72fdfb288f3edec6b957f25e57877db963da62c4..c1833bba173b6133b0970eb5dcd6cae4a4c69791 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,3 +68,4 @@ mechanisms/*.hpp
 build*
 
 commit.msg
+
diff --git a/mechanisms/generate.sh b/mechanisms/generate.sh
new file mode 100755
index 0000000000000000000000000000000000000000..89dfd5e277f9811bd91cdc363a61f41a1e81b706
--- /dev/null
+++ b/mechanisms/generate.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+#flags="-t cpu -O"
+flags="-t cpu"
+
+for mech in pas hh expsyn
+do
+    echo ../external/modparser/bin/modcc ${flags} -o ../include/mechanisms/$mech.hpp ./mod/$mech.mod
+    ../external/modparser/bin/modcc ${flags} -o ../include/mechanisms/$mech.hpp ./mod/$mech.mod
+done
diff --git a/miniapp/io.cpp b/miniapp/io.cpp
index 17756e2cf5fba0bafa70354b2f7ebe5294316422..344fd56135b232bef3563d6a3cc61b570868a360 100644
--- a/miniapp/io.cpp
+++ b/miniapp/io.cpp
@@ -1,10 +1,13 @@
+#include <algorithm>
 #include <exception>
 #include <fstream>
 #include <istream>
+#include <type_traits>
 
 #include <tclap/CmdLine.h>
 #include <json/src/json.hpp>
 
+#include <util/meta.hpp>
 #include <util/optional.hpp>
 
 #include "io.hpp"
@@ -12,20 +15,17 @@
 // Let TCLAP understand value arguments that are of an optional type.
 
 namespace TCLAP {
-
-template <typename V>
-struct ArgTraits<nest::mc::util::optional<V>> {
-    using ValueCategory = ValueLike;
-};
-
+    template <typename V>
+    struct ArgTraits<nest::mc::util::optional<V>> {
+        using ValueCategory = ValueLike;
+    };
 } // namespace TCLAP
 
 namespace nest {
 namespace mc {
 
-// Using static here because we do not want external linkage for this operator
-
 namespace util {
+    // Using static here because we do not want external linkage for this operator.
     template <typename V>
     static std::istream& operator>>(std::istream& I, optional<V>& v) {
         V u;
@@ -38,106 +38,259 @@ namespace util {
 
 namespace io {
 
-/// read simulation options from json file with name fname
-/// if file name is empty or if file is not a valid json file a default
-/// set of parameters is returned :
-///      1000 cells, 500 synapses per cell, 100 compartments per segment
+// Override annoying parameters listed back-to-front behaviour.
+//
+// TCLAP argument creation _prepends_ its arguments to the internal
+// list (_argList), where standard options --help etc. are already
+// pre-inserted.
+//
+// reorder_arguments() reverses the arguments to restore ordering,
+// and moves the standard options to the end.
+class CustomCmdLine: public TCLAP::CmdLine {
+public:
+    CustomCmdLine(const std::string &message, const std::string &version = "none"):
+        TCLAP::CmdLine(message, ' ', version, true)
+    {}
+
+    void reorder_arguments() {
+        _argList.reverse();
+        for (auto opt: {"help", "version", "ignore_rest"}) {
+            auto i = std::find_if(
+                _argList.begin(), _argList.end(),
+                [&opt](TCLAP::Arg* a) { return a->getName()==opt; });
+
+            if (i!=_argList.end()) {
+                auto a = *i;
+                _argList.erase(i);
+                _argList.push_back(a);
+            }
+        }
+    }
+};
+
+// Update an option value from command line argument if set.
+template <
+    typename T,
+    typename Arg,
+    typename = util::enable_if_t<std::is_base_of<TCLAP::Arg, Arg>::value>
+>
+static void update_option(T& opt, Arg& arg) {
+    if (arg.isSet()) {
+        opt = arg.getValue();
+    }
+}
+
+// Update an option value from json object if key present.
+template <typename T>
+static void update_option(T& opt, const nlohmann::json& j, const std::string& key) {
+    if (j.count(key)) {
+        opt = j[key];
+    }
+}
+
+// --- special case for string due to ambiguous overloading in json library.
+static void update_option(std::string& opt, const nlohmann::json& j, const std::string& key) {
+    if (j.count(key)) {
+        opt = j[key].get<std::string>();
+    }
+}
+
+// --- special case for optional values.
+template <typename T>
+static void update_option(util::optional<T>& opt, const nlohmann::json& j, const std::string& key) {
+    if (j.count(key)) {
+        auto value = j[key];
+        if (value.is_null()) {
+            opt = util::nothing;
+        }
+        else {
+            opt = value;
+        }
+    }
+}
+
+// Read options from (optional) json file and command line arguments.
 cl_options read_options(int argc, char** argv) {
 
-    // set default options
-    const cl_options defopts{"", 1000, 500, "expsyn", 100, 100., 0.025, false,
-                             false, 1.0, "trace_", util::nothing};
+    // Default options:
+    const cl_options defopts{
+        1000,       // number of cells
+        500,        // synapses_per_cell
+        "expsyn",   // synapse type
+        100,        // compartments_per_segment
+        100.,       // tfinal
+        0.025,      // dt
+        false,      // all_to_all
+        false,      // probe_soma_only
+        0.0,        // probe_ratio
+        "trace_",   // trace_prefix
+        util::nothing,  // trace_max_gid
+
+        // spike_output_parameters:
+        false,      // spike output
+        false,      // single_file_per_simulation
+        true,       // Overwrite outputfile if exists
+        "./",       // output path
+        "spikes",   // file name
+        "gdf"       // file extension
+    };
 
     cl_options options;
-    // parse command line arguments
+    std::string save_file = "";
+
+    // Parse command line arguments.
     try {
-        TCLAP::CmdLine cmd("mod2c performance benchmark harness", ' ', "0.1");
+        CustomCmdLine cmd("nest mc miniapp harness", "0.1");
 
+        TCLAP::ValueArg<std::string> ifile_arg(
+            "i", "ifile",
+            "read parameters from json-formatted file <file name>",
+            false, "","file name", cmd);
+        TCLAP::ValueArg<std::string> ofile_arg(
+            "o", "ofile",
+            "save parameters to json-formatted file <file name>",
+            false, "","file name", cmd);
         TCLAP::ValueArg<uint32_t> ncells_arg(
             "n", "ncells", "total number of cells in the model",
-            false, defopts.cells, "non negative integer", cmd);
+            false, defopts.cells, "integer", cmd);
         TCLAP::ValueArg<uint32_t> nsynapses_arg(
             "s", "nsynapses", "number of synapses per cell",
-            false, defopts.synapses_per_cell, "non negative integer", cmd);
+            false, defopts.synapses_per_cell, "integer", cmd);
         TCLAP::ValueArg<std::string> syntype_arg(
-            "S", "syntype", "type of synapse (expsyn or exp2syn)",
-            false, defopts.syn_type, "synapse type", cmd);
+            "S", "syntype", "specify synapse type: expsyn or exp2syn",
+            false, defopts.syn_type, "string", cmd);
         TCLAP::ValueArg<uint32_t> ncompartments_arg(
             "c", "ncompartments", "number of compartments per segment",
-            false, defopts.compartments_per_segment, "non negative integer", cmd);
-        TCLAP::ValueArg<std::string> ifile_arg(
-            "i", "ifile", "json file with model parameters",
-            false, "","file name string", cmd);
+            false, defopts.compartments_per_segment, "integer", cmd);
         TCLAP::ValueArg<double> tfinal_arg(
-            "t", "tfinal", "time to simulate in ms",
-            false, defopts.tfinal, "positive real number", cmd);
+            "t", "tfinal", "run simulation to <time> ms",
+            false, defopts.tfinal, "time", cmd);
         TCLAP::ValueArg<double> dt_arg(
-            "d", "dt", "time step size in ms",
-            false, defopts.dt, "positive real number", cmd);
+            "d", "dt", "set simulation time step to <time> ms",
+            false, defopts.dt, "time", cmd);
         TCLAP::SwitchArg all_to_all_arg(
             "m","alltoall","all to all network", cmd, false);
         TCLAP::ValueArg<double> probe_ratio_arg(
-            "p", "probe-ratio", "proportion of cells to probe",
-            false, defopts.probe_ratio, "real number in [0,1]", cmd);
+            "p", "probe-ratio", "proportion between 0 and 1 of cells to probe",
+            false, defopts.probe_ratio, "proportion", cmd);
         TCLAP::SwitchArg probe_soma_only_arg(
             "X", "probe-soma-only", "only probe cell somas, not dendrites", cmd, false);
         TCLAP::ValueArg<std::string> trace_prefix_arg(
-            "P", "trace-prefix", "write traces to files with this prefix",
+            "P", "prefix", "write traces to files with prefix <prefix>",
             false, defopts.trace_prefix, "stringr", cmd);
         TCLAP::ValueArg<util::optional<unsigned>> trace_max_gid_arg(
-            "T", "trace-max-gid", "only trace probes on cells up to this gid",
-            false, defopts.trace_max_gid, "unisgned integer", cmd);
+            "T", "trace-max-gid", "only trace probes on cells up to and including <gid>",
+            false, defopts.trace_max_gid, "gid", cmd);
+        TCLAP::SwitchArg spike_output_arg(
+            "f","spike_file_output","save spikes to file", cmd, false);
 
+        cmd.reorder_arguments();
         cmd.parse(argc, argv);
 
-        options.cells = ncells_arg.getValue();
-        options.synapses_per_cell = nsynapses_arg.getValue();
-        options.syn_type = syntype_arg.getValue();
-        options.compartments_per_segment = ncompartments_arg.getValue();
-        options.ifname = ifile_arg.getValue();
-        options.tfinal = tfinal_arg.getValue();
-        options.dt = dt_arg.getValue();
-        options.all_to_all = all_to_all_arg.getValue();
-        options.probe_ratio = probe_ratio_arg.getValue();
-        options.probe_soma_only = probe_soma_only_arg.getValue();
-        options.trace_prefix = trace_prefix_arg.getValue();
-        options.trace_max_gid = trace_max_gid_arg.getValue();
+        options = defopts;
+
+        std::string ifile_name = ifile_arg.getValue();
+        if (ifile_name != "") {
+            // Read parameters from specified JSON file first, to allow
+            // overriding arguments on the command line.
+            std::ifstream fid(ifile_name);
+            if (fid) {
+                try {
+                    nlohmann::json fopts;
+                    fid >> fopts;
+
+                    update_option(options.cells, fopts, "cells");
+                    update_option(options.synapses_per_cell, fopts, "synapses");
+                    update_option(options.syn_type, fopts, "syn_type");
+                    update_option(options.compartments_per_segment, fopts, "compartments");
+                    update_option(options.dt, fopts, "dt");
+                    update_option(options.tfinal, fopts, "tfinal");
+                    update_option(options.all_to_all, fopts, "all_to_all");
+                    update_option(options.probe_ratio, fopts, "probe_ratio");
+                    update_option(options.probe_soma_only, fopts, "probe_soma_only");
+                    update_option(options.trace_prefix, fopts, "trace_prefix");
+                    update_option(options.trace_max_gid, fopts, "trace_max_gid");
+
+                    // Parameters for spike output
+                    update_option(options.spike_file_output, fopts, "spike_file_output");
+                    if (options.spike_file_output) {
+                        update_option(options.single_file_per_rank, fopts, "single_file_per_rank");
+                        update_option(options.over_write, fopts, "over_write");
+                        update_option(options.output_path, fopts, "output_path");
+                        update_option(options.file_name, fopts, "file_name");
+                        update_option(options.file_extension, fopts, "file_extension");
+                    }
+
+                }
+                catch (std::exception& e) {
+                    throw model_description_error(
+                        "unable to parse parameters in "+ifile_name+": "+e.what());
+                }
+            }
+            else {
+                throw usage_error("unable to open model parameter file "+ifile_name);
+            }
+        }
+
+        update_option(options.cells, ncells_arg);
+        update_option(options.synapses_per_cell, nsynapses_arg);
+        update_option(options.syn_type, syntype_arg);
+        update_option(options.compartments_per_segment, ncompartments_arg);
+        update_option(options.tfinal, tfinal_arg);
+        update_option(options.dt, dt_arg);
+        update_option(options.all_to_all, all_to_all_arg);
+        update_option(options.probe_ratio, probe_ratio_arg);
+        update_option(options.probe_soma_only, probe_soma_only_arg);
+        update_option(options.trace_prefix, trace_prefix_arg);
+        update_option(options.trace_max_gid, trace_max_gid_arg);
+        update_option(options.spike_file_output, spike_output_arg);
+
+        save_file = ofile_arg.getValue();
     }
-    // catch any exceptions in command line handling
     catch (TCLAP::ArgException& e) {
         throw usage_error("error parsing command line argument "+e.argId()+": "+e.error());
     }
 
-    if (options.ifname != "") {
-        std::ifstream fid(options.ifname);
+    // Save option values if requested.
+    if (save_file != "") {
+        std::ofstream fid(save_file);
         if (fid) {
-            // read json data in input file
-            nlohmann::json fopts;
-            fid >> fopts;
-
             try {
-                options.cells = fopts["cells"];
-                options.synapses_per_cell = fopts["synapses"];
-                options.compartments_per_segment = fopts["compartments"];
-                options.dt = fopts["dt"];
-                options.tfinal = fopts["tfinal"];
-                options.all_to_all = fopts["all_to_all"];
+                nlohmann::json fopts;
+
+                fopts["cells"] = options.cells;
+                fopts["synapses"] = options.synapses_per_cell;
+                fopts["syn_type"] = options.syn_type;
+                fopts["compartments"] = options.compartments_per_segment;
+                fopts["dt"] = options.dt;
+                fopts["tfinal"] = options.tfinal;
+                fopts["all_to_all"] = options.all_to_all;
+                fopts["probe_ratio"] = options.probe_ratio;
+                fopts["probe_soma_only"] = options.probe_soma_only;
+                fopts["trace_prefix"] = options.trace_prefix;
+                if (options.trace_max_gid) {
+                    fopts["trace_max_gid"] = options.trace_max_gid.get();
+                }
+                else {
+                    fopts["trace_max_gid"] = nullptr;
+                }
+                fid << std::setw(3) << fopts << "\n";
+
             }
             catch (std::exception& e) {
                 throw model_description_error(
-                    "unable to parse parameters in "+options.ifname+": "+e.what());
+                    "unable to save parameters in "+save_file+": "+e.what());
             }
         }
         else {
-            throw usage_error("unable to open model parameter file "+options.ifname);
+            throw usage_error("unable to write to model parameter file "+save_file);
         }
     }
-
     return options;
 }
 
 std::ostream& operator<<(std::ostream& o, const cl_options& options) {
-    o << "simultion options:\n";
+    o << "simulation options:\n";
     o << "  cells                : " << options.cells << "\n";
     o << "  compartments/segment : " << options.compartments_per_segment << "\n";
     o << "  synapses/cell        : " << options.synapses_per_cell << "\n";
@@ -152,7 +305,6 @@ std::ostream& operator<<(std::ostream& o, const cl_options& options) {
        o << *options.trace_max_gid;
     }
     o << "\n";
-    o << "  input file name      : " << options.ifname << "\n";
 
     return o;
 }
diff --git a/miniapp/io.hpp b/miniapp/io.hpp
index 71af61810670d7a1f226d3eec2dabfd2f38ff20e..c18e688c33ceff0c3966b5d66ae8eb1680bc48a1 100644
--- a/miniapp/io.hpp
+++ b/miniapp/io.hpp
@@ -14,7 +14,6 @@ namespace io {
 
 // holds the options for a simulation run
 struct cl_options {
-    std::string ifname;
     uint32_t cells;
     uint32_t synapses_per_cell;
     std::string syn_type;
@@ -26,6 +25,14 @@ struct cl_options {
     double probe_ratio;
     std::string trace_prefix;
     util::optional<unsigned> trace_max_gid;
+
+    // Parameters for spike output
+    bool spike_file_output;
+    bool single_file_per_rank;
+    bool over_write;
+    std::string output_path;
+    std::string file_name;
+    std::string file_extension;
 };
 
 class usage_error: public std::runtime_error {
diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp
index b6a6e42b642268b8364974bbe45e307f8539c343..20082a7893aef381d8cfec365d51e80ae71043da 100644
--- a/miniapp/miniapp.cpp
+++ b/miniapp/miniapp.cpp
@@ -3,20 +3,23 @@
 #include <iostream>
 #include <fstream>
 #include <memory>
+#include <vector>
 
 #include <json/src/json.hpp>
 
 #include <common_types.hpp>
 #include <cell.hpp>
 #include <cell_group.hpp>
+#include <communication/communicator.hpp>
+#include <communication/global_policy.hpp>
 #include <fvm_cell.hpp>
+#include <io/exporter_spike_file.hpp>
 #include <mechanism_catalogue.hpp>
 #include <model.hpp>
-#include <threading/threading.hpp>
 #include <profiling/profiler.hpp>
-#include <communication/communicator.hpp>
-#include <communication/global_policy.hpp>
+#include <threading/threading.hpp>
 #include <util/ioutil.hpp>
+#include <util/nop.hpp>
 #include <util/optional.hpp>
 
 #include "io.hpp"
@@ -26,15 +29,17 @@
 using namespace nest::mc;
 
 using global_policy = communication::global_policy;
-
 using lowered_cell = fvm::fvm_cell<double, cell_local_size_type>;
 using model_type = model<lowered_cell>;
-using sample_trace_type = sample_trace<model_type::time_type, model_type::value_type>;
-
+using time_type = model_type::time_type;
+using sample_trace_type = sample_trace<time_type, model_type::value_type>;
+using file_export_type = io::exporter_spike_file<time_type, global_policy>;
 void banner();
 std::unique_ptr<recipe> make_recipe(const io::cl_options&, const probe_distribution&);
 std::unique_ptr<sample_trace_type> make_trace(cell_member_type probe_id, probe_spec probe);
 std::pair<cell_gid_type, cell_gid_type> distribute_cells(cell_size_type ncells);
+using communicator_type = communication::communicator<time_type, communication::global_policy>;
+using spike_type = typename communicator_type::spike_type;
 
 void write_trace_json(const sample_trace_type& trace, const std::string& prefix = "trace_");
 
@@ -61,9 +66,35 @@ int main(int argc, char** argv) {
         auto recipe = make_recipe(options, pdist);
         auto cell_range = distribute_cells(recipe->num_cells());
 
-        // build model from recipe
+
         model_type m(*recipe, cell_range.first, cell_range.second);
 
+        auto register_exporter = [] (const io::cl_options& options) {
+            return
+                util::make_unique<file_export_type>(
+                    options.file_name, options.output_path,
+                    options.file_extension, options.over_write);
+        };
+
+        // File output is depending on the input arguments
+        std::unique_ptr<file_export_type> file_exporter;
+        if (options.spike_file_output) {
+            if (options.single_file_per_rank) {
+                file_exporter = register_exporter(options);
+                m.set_local_spike_callback(
+                    [&](const std::vector<spike_type>& spikes) {
+                        file_exporter->output(spikes);
+                    });
+            }
+            else if(communication::global_policy::id()==0) {
+                file_exporter = register_exporter(options);
+                m.set_global_spike_callback(
+                    [&](const std::vector<spike_type>& spikes) {
+                       file_exporter->output(spikes);
+                    });
+            }
+        }
+
         // inject some artificial spikes, 1 per 20 neurons.
         std::vector<cell_gid_type> local_sources;
         cell_gid_type first_spike_cell = 20*((cell_range.first+19)/20);
@@ -169,7 +200,7 @@ std::unique_ptr<sample_trace_type> make_trace(cell_member_type probe_id, probe_s
     case probeKind::membrane_current:
         name = "i";
         units = "mA/cm²";
-        break; 
+        break;
     default: ;
     }
     name += probe.location.segment? "dend" : "soma";
@@ -197,5 +228,3 @@ void write_trace_json(const sample_trace_type& trace, const std::string& prefix)
     std::ofstream file(path);
     file << std::setw(1) << jrep << std::endl;
 }
-
-
diff --git a/nrn/generate_validation.sh b/nrn/generate_validation.sh
index 532a7cd738d53a1a0f8342d07f09c016b5e24857..15149cddc022aa47d2f94149fc4efc6c833e9ecd 100755
--- a/nrn/generate_validation.sh
+++ b/nrn/generate_validation.sh
@@ -1,5 +1,7 @@
-python2.7 ./soma.py
-python2.7 ./ball_and_stick.py
-python2.7 ./ball_and_3stick.py
-python2.7 ./simple_synapse.py --synapse exp2
-python2.7 ./simple_synapse.py --synapse exp
+#!/usr/bin/env bash
+
+python2 ./soma.py
+python2 ./ball_and_stick.py
+python2 ./ball_and_3stick.py
+python2 ./simple_synapse.py --synapse exp2
+python2 ./simple_synapse.py --synapse exp
diff --git a/src/algorithms.hpp b/src/algorithms.hpp
index 5456417ddaea0e0fe6c4e777a0fa8ebf3344c2ed..37eeaf370acd54ef326f70cc4ae17e46f1c6f2b0 100644
--- a/src/algorithms.hpp
+++ b/src/algorithms.hpp
@@ -139,8 +139,9 @@ std::vector<typename C::value_type> child_count(const C& parent_index)
 template<typename C>
 bool has_contiguous_compartments(const C& parent_index)
 {
+    using value_type = typename C::value_type;
     static_assert(
-        std::is_integral<typename C::value_type>::value,
+        std::is_integral<value_type>::value,
         "integral type required"
     );
 
@@ -149,9 +150,9 @@ bool has_contiguous_compartments(const C& parent_index)
     }
 
     auto num_child = child_count(parent_index);
-    for (auto i = 1u; i < parent_index.size(); ++i) {
+    for (auto i=1u; i < parent_index.size(); ++i) {
         auto p = parent_index[i];
-        if (num_child[p] == 1 && p != i-1) {
+        if (num_child[p]==1 && p!=value_type(i-1)) {
             return false;
         }
     }
diff --git a/src/communication/communicator.hpp b/src/communication/communicator.hpp
index 802d94f9d13053be78aa45721922e99ebe4b1c0f..7ae506aba03ba80a421b313193e9b00bc31b2e26 100644
--- a/src/communication/communicator.hpp
+++ b/src/communication/communicator.hpp
@@ -4,6 +4,7 @@
 #include <iostream>
 #include <vector>
 #include <random>
+#include <functional>
 
 #include <spike.hpp>
 #include <util/double_buffer.hpp>
@@ -90,11 +91,15 @@ public:
     /// Returns a vector of event queues, with one queue for each local cell group. The
     /// events in each queue are all events that must be delivered to targets in that cell
     /// group as a result of the global spike exchange.
-    std::vector<event_queue> exchange(const std::vector<spike_type>& local_spikes) {
+    std::vector<event_queue> exchange(const std::vector<spike_type>& local_spikes,
+        std::function<void(const std::vector<spike_type>&)> global_export_callback)
+    {
         // global all-to-all to gather a local copy of the global spike list on each node.
         auto global_spikes = communication_policy_.gather_spikes( local_spikes );
         num_spikes_ += global_spikes.size();
 
+        global_export_callback(global_spikes);
+
         // 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 = std::vector<event_queue>(num_groups_local());
diff --git a/src/io/exporter.hpp b/src/io/exporter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a5885b7b10841593059402a491ccf21148b78df3
--- /dev/null
+++ b/src/io/exporter.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <random>
+#include <string>
+
+#include <common_types.hpp>
+#include <spike.hpp>
+
+namespace nest {
+namespace mc {
+namespace io {
+
+// interface for exporters.
+// Exposes one virtual functions:
+//    do_export(vector<type>) receiving a vector of parameters to export
+
+template <typename Time, typename CommunicationPolicy>
+class exporter {
+
+public:
+    using time_type = Time;
+    using spike_type = spike<cell_member_type, time_type>;
+
+    // Performs the export of the data
+    virtual void output(const std::vector<spike_type>&) = 0;
+
+    // Returns the status of the exporter
+    virtual bool good() const = 0;
+};
+
+} //communication
+} // namespace mc
+} // namespace nest
diff --git a/src/io/exporter_spike_file.hpp b/src/io/exporter_spike_file.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..67c714209becbe140d2244a8a7e28552c41d7b8c
--- /dev/null
+++ b/src/io/exporter_spike_file.hpp
@@ -0,0 +1,102 @@
+#pragma once
+
+#include <fstream>
+#include <iomanip>
+#include <memory>
+#include <random>
+#include <stdexcept>
+#include <vector>
+
+#include <cstring>
+#include <cstdio>
+
+#include <common_types.hpp>
+#include <io/exporter.hpp>
+#include <spike.hpp>
+#include <util.hpp>
+
+namespace nest {
+namespace mc {
+namespace io {
+
+template <typename Time, typename CommunicationPolicy>
+class exporter_spike_file : public exporter<Time, CommunicationPolicy> {
+public:
+    using time_type = Time;
+    using spike_type = spike<cell_member_type, time_type>;
+    using communication_policy_type = CommunicationPolicy;
+
+    // Constructor
+    // over_write if true will overwrite the specified output file (default = true)
+    // output_path  relative or absolute path
+    // file_name    will be appended with "_x" with x the rank number
+    // file_extension  a seperator will be added automatically
+    exporter_spike_file(
+        const std::string& file_name,
+        const std::string& path,
+        const std::string& file_extension,
+        bool over_write=true)
+    {
+        file_path_ =
+            create_output_file_path(
+                file_name, path, file_extension, communication_policy_.id());
+
+        //test if the file exist and depending on over_write throw or delete
+        if (!over_write && file_exists(file_path_)) {
+            throw std::runtime_error(
+                "Tried opening file for writing but it exists and over_write is false: " + file_path_);
+        }
+
+        file_handle_.open(file_path_);
+    }
+
+    // Performs the a export of the spikes to file
+    // one id and spike time with 4 decimals after the comma on a
+    // line space separated
+    void output(const std::vector<spike_type>& spikes) override {
+        for (auto spike : spikes) {
+            char linebuf[45];
+            auto n =
+                std::snprintf(
+                    linebuf, sizeof(linebuf), "%u %.4f\n",
+                    unsigned{spike.source.gid}, float(spike.time));
+            file_handle_.write(linebuf, n);
+        }
+    }
+
+    bool good() const override {
+        return file_handle_.good();
+    }
+
+    // Creates an indexed filename
+    static std::string create_output_file_path(
+        const std::string& file_name,
+        const std::string& path,
+        const std::string& file_extension,
+        unsigned index)
+    {
+        return path + file_name + "_" +  std::to_string(index) + "." + file_extension;
+    }
+
+    // The name of the output path and file name.
+    // May be either relative or absolute path.
+    const std::string& file_path() const {
+        return file_path_;
+    }
+
+private:
+    bool file_exists(const std::string& file_path) {
+        std::ifstream fid(file_path);
+        return fid.good();
+    }
+
+    // Handle to opened file handle
+    std::ofstream file_handle_;
+    std::string file_path_;
+
+    communication_policy_type communication_policy_;
+};
+
+} //communication
+} // namespace mc
+} // namespace nest
diff --git a/src/model.hpp b/src/model.hpp
index 35b7029115c4ebbb4dce78bbb2611f923efc9648..85137f205c4634fd928ab82cdea44c9f837206d7 100644
--- a/src/model.hpp
+++ b/src/model.hpp
@@ -1,17 +1,20 @@
 #pragma once
 
-#include <cstdlib>
+#include <memory>
 #include <vector>
 
+#include <cstdlib>
+
 #include <common_types.hpp>
 #include <cell.hpp>
 #include <cell_group.hpp>
-#include <fvm_cell.hpp>
-#include <recipe.hpp>
-#include <thread_private_spike_store.hpp>
 #include <communication/communicator.hpp>
 #include <communication/global_policy.hpp>
+#include <fvm_cell.hpp>
 #include <profiling/profiler.hpp>
+#include <recipe.hpp>
+#include <thread_private_spike_store.hpp>
+#include <util/nop.hpp>
 
 #include "trace_sampler.hpp"
 
@@ -26,6 +29,8 @@ public:
     using value_type = typename cell_group_type::value_type;
     using communicator_type = communication::communicator<time_type, communication::global_policy>;
     using sampler_function = typename cell_group_type::sampler_function;
+    using spike_type = typename communicator_type::spike_type;
+    using spike_export_function = std::function<void(const std::vector<spike_type>&)>;
 
     struct probe_record {
         cell_member_type id;
@@ -130,7 +135,7 @@ public:
             auto update_cells = [&] () {
                 threading::parallel_for::apply(
                     0u, cell_groups_.size(),
-                    [&](unsigned i) {
+                     [&](unsigned i) {
                         auto &group = cell_groups_[i];
 
                         PE("stepping","events");
@@ -150,10 +155,18 @@ public:
             // the previous integration period, generating the postsynaptic
             // events that must be delivered at the start of the next
             // integration period at the latest.
+
+            //TODO:
+            //An improvement might be :
+            //the exchange method simply exchanges spikes, and does not generate the event queues.It returns a struct that has both 1) the global spike list 2) an integer vector that describes the distribution of spikes across the ranks
+            //another method called something like build_queues that takes this spike info and returns the local spikes
+            //    and the callbacks can then be called on the spike information directly in the model.
             auto exchange = [&] () {
                 PE("stepping", "exchange");
                 auto local_spikes = previous_spikes().gather();
-                future_events() = communicator_.exchange(local_spikes);
+                local_export_callback_(local_spikes);
+                future_events() =
+                    communicator_.exchange(local_spikes, global_export_callback_);
                 PL(2);
             };
 
@@ -201,6 +214,18 @@ public:
         return num_groups();
     }
 
+    // register a callback that will perform a export of the global
+    // spike vector
+    void set_global_spike_callback(spike_export_function export_callback) {
+        global_export_callback_ = export_callback;
+    }
+
+    // register a callback that will perform a export of the rank local
+    // spike vector
+    void set_local_spike_callback(spike_export_function export_callback) {
+        local_export_callback_ = export_callback;
+    }
+
 private:
     cell_gid_type cell_from_;
     cell_gid_type cell_to_;
@@ -216,6 +241,9 @@ private:
     using local_spike_store_type = thread_private_spike_store<time_type>;
     util::double_buffer< local_spike_store_type > local_spikes_;
 
+    spike_export_function global_export_callback_ = util::nop_function;
+    spike_export_function local_export_callback_ = util::nop_function;
+
     // Convenience functions that map the spike buffers and event queues onto
     // the appropriate integration interval.
     //
diff --git a/src/util/counter.hpp b/src/util/counter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d071752807f53fc72324367d4e4ea87e0dc6c7be
--- /dev/null
+++ b/src/util/counter.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+/* Present an integral value as an iterator, for integral-range 'containers' */
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+template <typename V, typename = typename std::enable_if<std::is_integral<V>::value>::type>
+struct counter {
+    using difference_type = V;
+    using value_type = V;
+    using pointer = const V*;
+    using reference = const V&;
+    using iterator_category = std::random_access_iterator_tag;
+
+    counter(): v_{} {}
+    counter(V v): v_{v} {}
+
+    counter(const counter&) = default;
+    counter(counter&&) = default;
+
+    counter& operator++() {
+        ++v_;
+        return *this;
+    }
+
+    counter operator++(int) {
+        counter c(*this);
+        ++v_;
+        return c;
+    }
+
+    counter& operator--() {
+        --v_;
+        return *this;
+    }
+
+    counter operator--(int) {
+        counter c(*this);
+        --v_;
+        return c;
+    }
+
+    counter& operator+=(difference_type n) {
+        v_ += n;
+        return *this;
+    }
+
+    counter& operator-=(difference_type n) {
+        v_ -= n;
+        return *this;
+    }
+
+    counter operator+(difference_type n) {
+        return counter(v_+n);
+    }
+
+    friend counter operator+(difference_type n, counter x) {
+        return counter(n+x.v_);
+    }
+
+    counter operator-(difference_type n) {
+        return counter(v_-n);
+    }
+
+    difference_type operator-(counter x) const {
+        return v_-x.v_;
+    }
+
+    value_type operator*() const { return v_; }
+
+    value_type operator[](difference_type n) const { return v_+n; }
+
+    bool operator==(counter x) const { return v_==x.v_; }
+    bool operator!=(counter x) const { return v_!=x.v_; }
+    bool operator<=(counter x) const { return v_<=x.v_; }
+    bool operator>=(counter x) const { return v_>=x.v_; }
+    bool operator<(counter x) const { return v_<x.v_; }
+    bool operator>(counter x) const { return v_>x.v_; }
+
+    counter& operator=(const counter&) = default;
+    counter& operator=(counter&&) = default;
+
+private:
+    V v_;
+};
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/debug.cpp b/src/util/debug.cpp
index 51c646bbcb38b1722e40766f4886a957094bd1bb..f2f039708a860c7fcb9ec65db7345b608a019dd9 100644
--- a/src/util/debug.cpp
+++ b/src/util/debug.cpp
@@ -14,8 +14,11 @@ namespace util {
 
 std::mutex global_debug_cerr_mutex;
 
-bool failed_assertion(const char* assertion, const char* file,
-                      int line, const char* func)
+bool abort_on_failed_assertion(
+    const char* assertion,
+    const char* file,
+    int line,
+    const char* func)
 {
     // Explicit flush, as we can't assume default buffering semantics on stderr/cerr,
     // and abort() might not flush streams.
@@ -26,8 +29,13 @@ bool failed_assertion(const char* assertion, const char* file,
     return false;
 }
 
-std::ostream& debug_emit_trace_leader(std::ostream& out, const char* file,
-                                      int line, const char* varlist)
+failed_assertion_handler_t global_failed_assertion_handler = abort_on_failed_assertion;
+
+std::ostream& debug_emit_trace_leader(
+    std::ostream& out,
+    const char* file,
+    int line,
+    const char* varlist)
 {
     iosfmt_guard guard(out);
 
diff --git a/src/util/debug.hpp b/src/util/debug.hpp
index f258b37123755c7ec7a7324641249ed35290e8bb..be2b3d51e4d7bf9b5d4c5fb9b7c78855c5e4b4c5 100644
--- a/src/util/debug.hpp
+++ b/src/util/debug.hpp
@@ -10,7 +10,17 @@ namespace nest {
 namespace mc {
 namespace util {
 
-bool failed_assertion(const char* assertion, const char* file, int line, const char* func);
+using failed_assertion_handler_t =
+    bool (*)(const char* assertion, const char* file, int line, const char* func);
+
+bool abort_on_failed_assertion(const char* assertion, const char* file, int line, const char* func);
+inline bool ignore_failed_assertion(const char*, const char*, int, const char*) {
+    return false;
+}
+
+// defaults to abort_on_failed_assertion;
+extern failed_assertion_handler_t global_failed_assertion_handler;
+
 std::ostream& debug_emit_trace_leader(std::ostream& out, const char* file, int line, const char* varlist);
 
 inline void debug_emit(std::ostream& out) {
@@ -66,7 +76,7 @@ void debug_emit_trace(const char* file, int line, const char* varlist, const Arg
 
     #define EXPECTS(condition) \
        (void)((condition) || \
-       nest::mc::util::failed_assertion(#condition, __FILE__, __LINE__, DEBUG_FUNCTION_NAME))
+       nest::mc::util::global_failed_assertion_handler(#condition, __FILE__, __LINE__, DEBUG_FUNCTION_NAME))
 #else
     #define EXPECTS(condition)
 #endif // def WITH_ASSERTIONS
diff --git a/src/util/either.hpp b/src/util/either.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..36932c96e8be340638c98eb582d06ffa93450828
--- /dev/null
+++ b/src/util/either.hpp
@@ -0,0 +1,387 @@
+#pragma once
+
+/*
+ * A type-safe discriminated union of two members.
+ *
+ * Returns true in a bool context if the first of the two types holds a value.
+ */
+
+#include <cstdlib>
+#include <type_traits>
+#include <stdexcept>
+#include <utility>
+
+#include "util/meta.hpp"
+#include "util/uninitialized.hpp"
+
+namespace nest {
+namespace mc {
+namespace util {
+
+struct either_invalid_access: std::runtime_error {
+    explicit either_invalid_access(const std::string& what_str)
+        : std::runtime_error(what_str)
+    {}
+
+    either_invalid_access()
+        : std::runtime_error("access of unconstructed value in either")
+    {}
+};
+
+namespace detail {
+    template <typename A, typename B>
+    struct either_data {
+        union {
+            uninitialized<A> ua;
+            uninitialized<B> ub;
+        };
+
+        either_data() = default;
+
+        either_data(const either_data&) = delete;
+        either_data(either_data&&) = delete;
+        either_data& operator=(const either_data&) = delete;
+        either_data& operator=(either_data&&) = delete;
+    };
+
+    template <std::size_t, typename A, typename B> struct either_select;
+
+    template <typename A, typename B>
+    struct either_select<0, A, B> {
+        using type = uninitialized<A>;
+        static type& field(either_data<A, B>& data) { return data.ua; }
+        static const type& field(const either_data<A, B>& data) { return data.ua; }
+    };
+
+    template <typename A, typename B>
+    struct either_select<1, A, B> {
+        using type = uninitialized<B>;
+        static type& field(either_data<A, B>& data) { return data.ub; }
+        static const type& field(const either_data<A, B>& data) { return data.ub; }
+    };
+
+    template <std::size_t I, typename A, typename B>
+    struct either_get: either_select<I, A, B> {
+        using typename either_select<I, A, B>::type;
+        using either_select<I, A, B>::field;
+
+        static typename type::reference unsafe_get(either_data<A, B>& data) {
+            return field(data).ref();
+        }
+
+        static typename type::const_reference unsafe_get(const either_data<A, B>& data) {
+            return field(data).cref();
+        }
+
+        static typename type::reference unsafe_get(char which, either_data<A, B>& data) {
+            if (I!=which) {
+                throw either_invalid_access();
+            }
+            return field(data).ref();
+        }
+
+        static typename type::const_reference unsafe_get(char which, const either_data<A, B>& data) {
+            if (I!=which) {
+                throw either_invalid_access();
+            }
+            return field(data).cref();
+        }
+
+        static typename type::pointer ptr(char which, either_data<A, B>& data) {
+            return I==which? field(data).ptr(): nullptr;
+        }
+
+        static typename type::const_pointer ptr(char which, const either_data<A, B>& data) {
+            return I==which? field(data).cptr(): nullptr;
+        }
+    };
+} // namespace detail
+
+constexpr std::size_t variant_npos = -1; // emulating C++17 variant type 
+
+template <typename A, typename B>
+class either: public detail::either_data<A, B> {
+    using base = detail::either_data<A, B>;
+    using base::ua;
+    using base::ub;
+
+    template <std::size_t I>
+    using getter = detail::either_get<I, A, B>;
+
+    unsigned char which;
+
+public:
+    // default ctor if A is default-constructible or A is not and B is.
+    template <
+        typename A_ = A,
+        bool a_ = std::is_default_constructible<A_>::value,
+        bool b_ = std::is_default_constructible<B>::value,
+        typename = enable_if_t<a_ || (!a_ && b_)>,
+        std::size_t w_ = a_? 0: 1
+    >
+    either() noexcept(std::is_nothrow_default_constructible<typename getter<w_>::type>::value):
+        which(w_)
+    {
+        getter<w_>::field(*this).construct();
+    }
+
+    // implicit constructors from A and B values by copy or move
+    either(const A& a) noexcept(std::is_nothrow_copy_constructible<A>::value): which(0) {
+        getter<0>::field(*this).construct(a);
+    }
+
+    template <
+        typename B_ = B,
+        typename = enable_if_t<!std::is_same<A, B_>::value>
+    >
+    either(const B& b) noexcept(std::is_nothrow_copy_constructible<B>::value): which(1) {
+        getter<1>::field(*this).construct(b);
+    }
+
+    either(A&& a) noexcept(std::is_nothrow_move_constructible<A>::value): which(0) {
+        getter<0>::field(*this).construct(std::move(a));
+    }
+
+    template <
+        typename B_ = B,
+        typename = enable_if_t<!std::is_same<A, B_>::value>
+    >
+    either(B&& b) noexcept(std::is_nothrow_move_constructible<B>::value): which(1) {
+        getter<1>::field(*this).construct(std::move(b));
+    }
+
+    // copy constructor
+    either(const either& x)
+        noexcept(std::is_nothrow_copy_constructible<A>::value &&
+            std::is_nothrow_copy_constructible<B>::value):
+        which(x.which)
+    {
+        if (which==0) {
+            getter<0>::field(*this).construct(x.unsafe_get<0>());
+        }
+        else if (which==1) {
+            getter<1>::field(*this).construct(x.unsafe_get<1>());
+        }
+    }
+
+    // move constructor
+    either(either&& x)
+        noexcept(std::is_nothrow_move_constructible<A>::value &&
+            std::is_nothrow_move_constructible<B>::value):
+        which(x.which)
+    {
+        if (which==0) {
+            getter<0>::field(*this).construct(std::move(x.unsafe_get<0>()));
+        }
+        else if (which==1) {
+            getter<1>::field(*this).construct(std::move(x.unsafe_get<1>()));
+        }
+    }
+
+    // copy assignment
+    either& operator=(const either& x) {
+        if (this==&x) {
+            return *this;
+        }
+
+        switch (which) {
+        case 0:
+            if (x.which==0) {
+                unsafe_get<0>() = x.unsafe_get<0>();
+            }
+            else {
+                if (x.which==1) {
+                    B b_tmp(x.unsafe_get<1>());
+                    getter<0>::field(*this).destruct();
+                    which = (unsigned char)variant_npos;
+                    getter<1>::field(*this).construct(std::move(b_tmp));
+                    which = 1;
+                }
+                else {
+                    getter<0>::field(*this).destruct();
+                    which = (unsigned char)variant_npos;
+                }
+            }
+            break;
+        case 1:
+            if (x.which==1) {
+                unsafe_get<1>() = x.unsafe_get<1>();
+            }
+            else {
+                if (x.which==0) {
+                    A a_tmp(x.unsafe_get<0>());
+                    getter<1>::field(*this).destruct();
+                    which = (unsigned char)variant_npos;
+                    getter<0>::field(*this).construct(std::move(a_tmp));
+                    which = 0;
+                }
+                else {
+                    getter<1>::field(*this).destruct();
+                    which = (unsigned char)variant_npos;
+                }
+            }
+            break;
+        default: // variant_npos
+            if (x.which==0) {
+                getter<0>::field(*this).construct(x.unsafe_get<0>());
+            }
+            else if (x.which==1) {
+                getter<1>::field(*this).construct(x.unsafe_get<1>());
+            }
+            break;
+        }
+        return *this;
+    }
+
+    // move assignment
+    either& operator=(either&& x) {
+        if (this==&x) {
+            return *this;
+        }
+
+        switch (which) {
+        case 0:
+            if (x.which==0) {
+                unsafe_get<0>() = std::move(x.unsafe_get<0>());
+            }
+            else {
+                which = (unsigned char)variant_npos;
+                getter<0>::field(*this).destruct();
+                if (x.which==1) {
+                    getter<1>::field(*this).construct(std::move(x.unsafe_get<1>()));
+                    which = 1;
+                }
+            }
+            break;
+        case 1:
+            if (x.which==1) {
+                unsafe_get<1>() = std::move(x.unsafe_get<1>());
+            }
+            else {
+                which = (unsigned char)variant_npos;
+                getter<1>::field(*this).destruct();
+                if (x.which==0) {
+                    getter<0>::field(*this).construct(std::move(x.unsafe_get<0>()));
+                    which = 0;
+                }
+            }
+            break;
+        default: // variant_npos
+            if (x.which==0) {
+                getter<0>::field(*this).construct(std::move(x.unsafe_get<0>()));
+            }
+            else if (x.which==1) {
+                getter<1>::field(*this).construct(std::move(x.unsafe_get<1>()));
+            }
+            break;
+        }
+        return *this;
+    }
+
+    // unchecked element access
+    template <std::size_t I>
+    typename getter<I>::type::reference unsafe_get() {
+        return getter<I>::unsafe_get(*this);
+    }
+
+    template <std::size_t I>
+    typename getter<I>::type::const_reference unsafe_get() const {
+        return getter<I>::unsafe_get(*this);
+    }
+
+    // checked element access
+    template <std::size_t I>
+    typename getter<I>::type::reference get() {
+        return getter<I>::unsafe_get(which, *this);
+    }
+
+    template <std::size_t I>
+    typename getter<I>::type::const_reference get() const {
+        return getter<I>::unsafe_get(which, *this);
+    }
+
+    // convenience getter aliases
+    typename getter<0>::type::reference first() { return get<0>(); }
+    typename getter<0>::type::const_reference first() const { return get<0>(); }
+
+    typename getter<1>::type::reference second() { return get<1>(); }
+    typename getter<1>::type::const_reference second() const  { return get<1>(); }
+
+    // pointer to element access: return nullptr if it does not hold this item
+    template <std::size_t I>
+    auto ptr() -> decltype(getter<I>::ptr(which, *this)) {
+        return getter<I>::ptr(which, *this);
+    }
+
+    template <std::size_t I>
+    auto ptr() const -> decltype(getter<I>::ptr(which, *this)) {
+        return getter<I>::ptr(which, *this);
+    }
+
+    // true in bool context if holds first alternative
+    constexpr operator bool() const { return which==0; }
+
+    constexpr bool valueless_by_exception() const noexcept {
+        return which==(unsigned char)variant_npos;
+    }
+
+    constexpr std::size_t index() const noexcept {
+        return which;
+    }
+
+    ~either() {
+        if (which==0) {
+            getter<0>::field(*this).destruct();
+        }
+        else if (which==1) {
+            getter<1>::field(*this).destruct();
+        }
+    }
+
+    // comparison operators follow C++17 variant semantics
+    bool operator==(const either& x) const {
+        return index()==x.index() &&
+           index()==0? unsafe_get<0>()==x.unsafe_get<0>():
+           index()==1? unsafe_get<1>()==x.unsafe_get<1>():
+           true;
+    }
+
+    bool operator!=(const either& x) const {
+        return index()!=x.index() ||
+           index()==0? unsafe_get<0>()!=x.unsafe_get<0>():
+           index()==1? unsafe_get<1>()!=x.unsafe_get<1>():
+           false;
+    }
+
+    bool operator<(const either& x) const {
+        return !x.valueless_by_exception() &&
+           index()==0? (x.index()==1 || unsafe_get<0>()<x.unsafe_get<0>()):
+           index()==1? (x.index()!=0 && unsafe_get<1>()<x.unsafe_get<1>()):
+           true;
+    }
+
+    bool operator>=(const either& x) const {
+        return x.valueless_by_exception() ||
+           index()==0? (x.index()!=1 && unsafe_get<0>()>=x.unsafe_get<0>()):
+           index()==1? (x.index()==0 || unsafe_get<1>()>=x.unsafe_get<1>()):
+           false;
+    }
+
+    bool operator<=(const either& x) const {
+        return valueless_by_exception() ||
+           x.index()==0? (index()!=1 && unsafe_get<0>()<=x.unsafe_get<0>()):
+           x.index()==1? (index()==0 || unsafe_get<1>()<=x.unsafe_get<1>()):
+           false;
+    }
+
+    bool operator>(const either& x) const {
+        return !valueless_by_exception() &&
+           x.index()==0? (index()==1 || unsafe_get<0>()>x.unsafe_get<0>()):
+           x.index()==1? (index()!=0 && unsafe_get<1>()>x.unsafe_get<1>()):
+           true;
+    }
+};
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/iterutil.hpp b/src/util/iterutil.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a9fd159ffe9f75eddf57f2adb5c4d42cb9e7861
--- /dev/null
+++ b/src/util/iterutil.hpp
@@ -0,0 +1,178 @@
+#pragma once
+
+/*
+ * Utilities and base classes to help with
+ * implementing iterators and iterator adaptors.
+ */
+
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include <util/meta.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+/*
+ * Return the iterator reachable from iter such that
+ * std::next(iter)==end
+ *
+ * Two implementations: the first applies generally, while the
+ * second is used when we can just return std::prev(end).
+ */
+template <typename I, typename E>
+enable_if_t<
+    is_forward_iterator<I>::value &&
+        (!is_bidirectional_iterator<E>::value || !std::is_constructible<I, E>::value),
+    I>
+upto(I iter, E end) {
+    I j = iter;
+    while (j!=end) {
+        iter = j;
+        ++j;
+    }
+    return iter;
+}
+
+template <typename I, typename E>
+enable_if_t<is_bidirectional_iterator<E>::value && std::is_constructible<I, E>::value, I>
+upto(I iter, E end) {
+    return iter==I{end}? iter: I{std::prev(end)};
+}
+
+/*
+ * Provide a proxy object for operator->() for iterator adaptors that
+ * present rvalues on dereference.
+ */
+template <typename V>
+struct pointer_proxy: public V {
+    pointer_proxy(const V& v): V{v} {}
+    pointer_proxy(V&& v): V{std::move(v)} {}
+    const V* operator->() const { return this; }
+};
+
+/*
+ * Base class (using CRTP) for iterator adaptors that
+ * perform a transformation or present a proxy for
+ * an underlying iterator.
+ *
+ * Supplies default implementations for iterator concepts
+ * in terms of the derived class' methods and the
+ * inner iterator.
+ *
+ * Derived class must provide implementations for:
+ *   operator*()
+ *   operator[](difference_type)
+ *   inner()   // provides access to wrapped iterator
+ */
+
+template <typename Derived, typename I>
+class iterator_adaptor {
+protected:
+    Derived& derived() { return static_cast<Derived&>(*this); }
+    const Derived& derived() const { return static_cast<const Derived&>(*this); }
+
+private:
+    // Access to inner iterator provided by derived class.
+    I& inner() { return derived().inner(); }
+    const I& inner() const { return derived().inner(); }
+
+public:
+    using value_type = typename std::iterator_traits<I>::value_type;
+    using difference_type = typename std::iterator_traits<I>::difference_type;
+    using iterator_category = typename std::iterator_traits<I>::iterator_category;
+    using pointer = typename std::iterator_traits<I>::pointer;
+    using reference = typename std::iterator_traits<I>::reference;
+
+    iterator_adaptor() = default;
+
+    // forward and input iterator requirements
+
+    I operator->() { return inner(); }
+    I operator->() const { return inner(); }
+
+    Derived& operator++() {
+        ++inner();
+        return derived();
+    }
+
+    Derived operator++(int) {
+        Derived c(derived());
+        ++derived();
+        return c;
+    }
+
+    bool operator==(const Derived& x) const {
+        return inner()==x.inner();
+    }
+
+    bool operator!=(const Derived& x) const {
+        return !(derived()==x);
+    }
+
+    // bidirectional iterator requirements
+
+    Derived& operator--() {
+        --inner();
+        return derived();
+    }
+
+    Derived operator--(int) {
+        Derived c(derived());
+        --derived();
+        return c;
+    }
+
+    // random access iterator requirements
+
+    Derived& operator+=(difference_type n) {
+        inner() += n;
+        return derived();
+    }
+
+    Derived operator+(difference_type n) const {
+        Derived c(derived());
+        return c += n;
+    }
+
+    friend Derived operator+(difference_type n, const Derived& x) {
+        return x+n;
+    }
+
+    Derived& operator-=(difference_type n) {
+        inner() -= n;
+        return *this;
+    }
+
+    Derived operator-(difference_type n) const {
+        Derived c(derived());
+        return c -= n;
+    }
+
+    difference_type operator-(const Derived& x) const {
+        return inner()-x.inner();
+    }
+
+    bool operator<(const Derived& x) const {
+        return inner()<x.inner();
+    }
+
+    bool operator<=(const Derived& x) const {
+        return derived()<x || derived()==x;
+    }
+
+    bool operator>=(const Derived& x) const {
+        return !(derived()<x);
+    }
+
+    bool operator>(const Derived& x) const {
+        return !(derived()<=x);
+    }
+};
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/meta.hpp b/src/util/meta.hpp
index 2e22e31ceda0ff83588625486ec7a36161569733..7b1fb0d6f228175377242b607a111457ed90b414 100644
--- a/src/util/meta.hpp
+++ b/src/util/meta.hpp
@@ -2,6 +2,7 @@
 
 /* Type utilities and convenience expressions.  */
 
+#include <iterator>
 #include <type_traits>
 
 namespace nest {
@@ -13,8 +14,45 @@ namespace util {
 template <typename T>
 using result_of_t = typename std::result_of<T>::type;
 
-template <bool V>
-using enable_if_t = typename std::enable_if<V>::type;
+template <bool V, typename R = void>
+using enable_if_t = typename std::enable_if<V, R>::type;
+
+template <class...>
+using void_t = void;
+
+template <typename T>
+using decay_t = typename std::decay<T>::type;
+
+template <typename X>
+std::size_t size(const X& x) { return x.size(); }
+
+template <typename X, std::size_t N>
+constexpr std::size_t size(X (&)[N]) { return N; }
+
+template <typename T>
+constexpr auto cbegin(const T& c) -> decltype(std::begin(c)) {
+    return std::begin(c);
+}
+
+template <typename T>
+constexpr auto cend(const T& c) -> decltype(std::end(c)) {
+    return std::end(c);
+}
+
+// Types associated with a container or sequence
+
+template <typename Seq>
+struct sequence_traits {
+    using iterator = decltype(std::begin(std::declval<Seq&>()));
+    using const_iterator = decltype(cbegin(std::declval<Seq&>()));
+    using value_type = typename std::iterator_traits<iterator>::value_type;
+    using reference = typename std::iterator_traits<iterator>::reference;
+    using difference_type = typename std::iterator_traits<iterator>::difference_type;
+    using size_type = decltype(size(std::declval<Seq&>()));
+    // for use with heterogeneous ranges
+    using sentinel = decltype(std::end(std::declval<Seq&>()));
+    using const_sentinel = decltype(cend(std::declval<Seq&>()));
+};
 
 // Convenience short cuts
 
@@ -26,10 +64,91 @@ template <typename T>
 using enable_if_move_constructible_t =
     enable_if_t<std::is_move_constructible<T>::value>;
 
+template <typename T>
+using enable_if_default_constructible_t =
+    enable_if_t<std::is_default_constructible<T>::value>;
+
 template <typename... T>
 using enable_if_constructible_t =
     enable_if_t<std::is_constructible<T...>::value>;
 
+template <typename T>
+using enable_if_copy_assignable_t =
+    enable_if_t<std::is_copy_assignable<T>::value>;
+
+template <typename T>
+using enable_if_move_assignable_t =
+    enable_if_t<std::is_move_assignable<T>::value>;
+
+// Iterator class test
+// (might not be portable before C++17)
+
+template <typename T, typename = void>
+struct is_iterator: public std::false_type {};
+
+template <typename T>
+struct is_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>>:
+    public std::true_type {};
+
+template <typename T>
+using is_iterator_t = typename is_iterator<T>::type;
+
+// Random access iterator test
+
+template <typename T, typename = void>
+struct is_random_access_iterator: public std::false_type {};
+
+template <typename T>
+struct is_random_access_iterator<T, enable_if_t<
+        std::is_same<
+            std::random_access_iterator_tag,
+            typename std::iterator_traits<T>::iterator_category>::value
+    >> : public std::true_type {};
+
+template <typename T>
+using is_random_access_iterator_t = typename is_random_access_iterator<T>::type;
+
+// Bidirectional iterator test
+
+template <typename T, typename = void>
+struct is_bidirectional_iterator: public std::false_type {};
+
+template <typename T>
+struct is_bidirectional_iterator<T, enable_if_t<
+        std::is_same<
+            std::random_access_iterator_tag,
+            typename std::iterator_traits<T>::iterator_category>::value
+        ||
+        std::is_same<
+            std::bidirectional_iterator_tag,
+            typename std::iterator_traits<T>::iterator_category>::value
+    >> : public std::true_type {};
+
+template <typename T>
+using is_bidirectional_iterator_t = typename is_bidirectional_iterator<T>::type;
+
+// Forward iterator test
+
+template <typename T, typename = void>
+struct is_forward_iterator: public std::false_type {};
+
+template <typename T>
+struct is_forward_iterator<T, enable_if_t<
+        std::is_same<
+            std::random_access_iterator_tag,
+            typename std::iterator_traits<T>::iterator_category>::value
+        ||
+        std::is_same<
+            std::bidirectional_iterator_tag,
+            typename std::iterator_traits<T>::iterator_category>::value
+        ||
+        std::is_same<
+            std::forward_iterator_tag,
+            typename std::iterator_traits<T>::iterator_category>::value
+    >> : public std::true_type {};
+
+template <typename T>
+using is_forward_iterator_t = typename is_forward_iterator<T>::type;
 
 } // namespace util
 } // namespace mc
diff --git a/src/util/nop.hpp b/src/util/nop.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ec568b9d5fe9bfe4904b23ea6babcdf8b4ca84e
--- /dev/null
+++ b/src/util/nop.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+/*
+ * Provide object that implicitly converts to
+ * a std::function object that does nothing but return a
+ * default-constructed type or void.
+ */
+
+#include <functional>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+struct nop_function_t {
+    template <typename R, typename... Args>
+    operator std::function<R (Args...)>() const {
+        return [](Args...) { return R{}; };
+    }
+
+    template <typename... Args>
+    operator std::function<void (Args...)>() const {
+        return [](Args...) { };
+    }
+
+    // keep clang happy: see CWG issue #253,
+    // http://open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#253
+    constexpr nop_function_t() {}
+};
+
+static constexpr nop_function_t nop_function;
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/optional.hpp b/src/util/optional.hpp
index 6b4d0f8b66cbdcf24c59914466a01621895ca2bb..40b40d7c3ff27352638c789575172a5cd5fee8d4 100644
--- a/src/util/optional.hpp
+++ b/src/util/optional.hpp
@@ -38,16 +38,6 @@ struct optional_unset_error: std::runtime_error {
     {}
 };
 
-struct optional_invalid_dereference: std::runtime_error {
-    explicit optional_invalid_dereference(const std::string& what_str)
-        : std::runtime_error(what_str)
-    {}
-
-    optional_invalid_dereference()
-        : std::runtime_error("derefernce of optional<void> value")
-    {}
-};
-
 struct nothing_t {};
 constexpr nothing_t nothing{};
 
@@ -68,21 +58,21 @@ namespace detail {
     struct optional_tag {};
 
     template <typename X>
-    using is_optional = std::is_base_of<optional_tag, typename std::decay<X>::type>;
+    using is_optional = std::is_base_of<optional_tag, decay_t<X>>;
 
-    template <typename D,typename X>
+    template <typename D, typename X>
     struct wrapped_type_impl {
         using type = X;
     };
 
-    template <typename D,typename X>
-    struct wrapped_type_impl<optional<D>,X> {
+    template <typename D, typename X>
+    struct wrapped_type_impl<optional<D>, X> {
         using type = D;
     };
 
     template <typename X>
     struct wrapped_type {
-       using type = typename wrapped_type_impl<typename std::decay<X>::type,X>::type;
+       using type = typename wrapped_type_impl<decay_t<X>, X>::type;
     };
 
     template <typename X>
@@ -96,10 +86,10 @@ namespace detail {
         using data_type = util::uninitialized<X>;
 
     public:
-        using reference_type = typename data_type::reference_type;
-        using const_reference_type = typename data_type::const_reference_type;
-        using pointer_type = typename data_type::pointer_type;
-        using const_pointer_type = typename data_type::const_pointer_type;
+        using reference = typename data_type::reference;
+        using const_reference = typename data_type::const_reference;
+        using pointer = typename data_type::pointer;
+        using const_pointer = typename data_type::const_pointer;
 
     protected:
         bool set;
@@ -114,8 +104,8 @@ namespace detail {
             }
         }
 
-        reference_type       ref()       { return data.ref(); }
-        const_reference_type ref() const { return data.cref(); }
+        reference       ref()       { return data.ref(); }
+        const_reference ref() const { return data.cref(); }
 
     public:
         ~optional_base() {
@@ -124,28 +114,24 @@ namespace detail {
             }
         }
 
-        const_pointer_type operator->() const { return data.ptr(); }
-        pointer_type       operator->()       { return data.ptr(); }
+        pointer operator->() { return data.ptr(); }
+        const_pointer operator->() const { return data.ptr(); }
 
-        const_reference_type operator*() const { return ref(); }
-        reference_type       operator*()       { return ref(); }
+        reference operator*() { return ref(); }
+        const_reference operator*() const { return ref(); }
 
-        reference_type get() {
-            if (set) {
-                return ref();
-            }
-            else {
+        reference get() {
+            if (!set) {
                 throw optional_unset_error();
             }
+            return ref();
         }
 
-        const_reference_type get() const {
-            if (set) {
-                return ref();
-            }
-            else {
+        const_reference get() const {
+            if (!set) {
                 throw optional_unset_error();
             }
+            return ref();
         }
 
         explicit operator bool() const { return set; }
@@ -206,16 +192,16 @@ namespace detail {
     private:
         template <typename R, bool F_void_return>
         struct bind_impl {
-            template <typename DT,typename F>
-            static R bind(DT& d,F&& f) {
+            template <typename DT, typename F>
+            static R bind(DT& d, F&& f) {
                 return R(d.apply(std::forward<F>(f)));
             }
         };
 
         template <typename R>
-        struct bind_impl<R,true> {
-            template <typename DT,typename F>
-            static R bind(DT& d,F&& f) {
+        struct bind_impl<R, true> {
+            template <typename DT, typename F>
+            static R bind(DT& d, F&& f) {
                 d.apply(std::forward<F>(f));
                 return R(true);
             }
@@ -243,30 +229,32 @@ struct optional: detail::optional_base<X> {
     using base::reset;
     using base::data;
 
-    optional(): base() {}
-    optional(nothing_t): base() {}
+    optional() noexcept: base() {}
+    optional(nothing_t) noexcept: base() {}
 
-    template <
-        typename Y = X,
-        typename = enable_if_copy_constructible_t<Y>
-    >
-    optional(const X& x): base(true, x) {}
+    optional(const X& x)
+        noexcept(std::is_nothrow_copy_constructible<X>::value): base(true, x) {}
 
-    template <
-        typename Y = X,
-        typename = enable_if_move_constructible_t<Y>
-    >
-    optional(X&& x): base(true, std::move(x)) {}
+    optional(X&& x)
+        noexcept(std::is_nothrow_move_constructible<X>::value): base(true, std::move(x)) {}
 
     optional(const optional& ot): base(ot.set, ot.ref()) {}
 
     template <typename T>
-    optional(const optional<T>& ot): base(ot.set, ot.ref()) {}
+    optional(const optional<T>& ot)
+        noexcept(std::is_nothrow_constructible<X, T>::value): base(ot.set, ot.ref()) {}
 
-    optional(optional&& ot): base(ot.set, std::move(ot.ref())) {}
+    optional(optional&& ot)
+        noexcept(std::is_nothrow_move_constructible<X>::value): base(ot.set, std::move(ot.ref())) {}
 
     template <typename T>
-    optional(optional<T>&& ot): base(ot.set, std::move(ot.ref())) {}
+    optional(optional<T>&& ot)
+        noexcept(std::is_nothrow_constructible<X, T&&>::value): base(ot.set, std::move(ot.ref())) {}
+
+    optional& operator=(nothing_t) {
+        reset();
+        return *this;
+    }
 
     template <
         typename Y,
@@ -331,13 +319,19 @@ struct optional<X&>: detail::optional_base<X&> {
     using base::set;
     using base::ref;
     using base::data;
+    using base::reset;
 
-    optional(): base() {}
-    optional(nothing_t): base() {}
-    optional(X&x): base(true,x) {}
+    optional() noexcept: base() {}
+    optional(nothing_t) noexcept: base() {}
+    optional(X& x) noexcept: base(true, x) {}
 
     template <typename T>
-    optional(optional<T&>& ot): base(ot.set,ot.ref()) {}
+    optional(optional<T&>& ot) noexcept: base(ot.set, ot.ref()) {}
+
+    optional& operator=(nothing_t) {
+        reset();
+        return *this;
+    }
 
     template <typename Y>
     optional& operator=(Y& y) {
@@ -364,14 +358,20 @@ template <>
 struct optional<void>: detail::optional_base<void> {
     using base = detail::optional_base<void>;
     using base::set;
+    using base::reset;
 
     optional(): base() {}
 
     template <typename T>
-    optional(T): base(true,true) {}
+    optional(T): base(true, true) {}
 
     template <typename T>
-    optional(const optional<T>& o): base(o.set,true) {}
+    optional(const optional<T>& o): base(o.set, true) {}
+
+    optional& operator=(nothing_t) {
+        reset();
+        return *this;
+    }
 
     template <typename T>
     optional& operator=(T) {
@@ -394,7 +394,7 @@ struct optional<void>: detail::optional_base<void> {
 };
 
 
-template <typename A,typename B>
+template <typename A, typename B>
 typename std::enable_if<
     detail::is_optional<A>::value || detail::is_optional<B>::value,
     optional<
@@ -404,16 +404,16 @@ typename std::enable_if<
         >::type
     >
 >::type
-operator|(A&& a,B&& b) {
+operator|(A&& a, B&& b) {
     return detail::decay_bool(a) ? a : b;
 }
 
-template <typename A,typename B>
+template <typename A, typename B>
 typename std::enable_if<
     detail::is_optional<A>::value || detail::is_optional<B>::value,
     optional<detail::wrapped_type_t<B>>
 >::type
-operator&(A&& a,B&& b) {
+operator&(A&& a, B&& b) {
     using result_type = optional<detail::wrapped_type_t<B>>;
     return a ? b: result_type();
 }
diff --git a/src/util/partition.hpp b/src/util/partition.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b35f81de8fa1f40217e331dd96e09a11eba1c174
--- /dev/null
+++ b/src/util/partition.hpp
@@ -0,0 +1,165 @@
+#pragma once
+
+#include <iterator>
+#include <stdexcept>
+#include <type_traits>
+
+#include <util/either.hpp>
+#include <util/meta.hpp>
+#include <util/partition_iterator.hpp>
+#include <util/range.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+struct invalid_partition: std::runtime_error {
+    explicit invalid_partition(const std::string& what): std::runtime_error(what) {}
+    explicit invalid_partition(const char* what): std::runtime_error(what) {}
+};
+
+/*
+ * Present a sequence with monotically increasing values as a partition,
+ */
+template <typename I>
+class partition_range: public range<partition_iterator<I>> {
+    using base = range<partition_iterator<I>>;
+    using inner_value_type = typename std::iterator_traits<I>::value_type;
+
+public:
+    using typename base::iterator;
+    using typename base::value_type;
+    using base::left;
+    using base::right;
+    using base::front;
+    using base::back;
+    using base::empty;
+
+    template <typename Seq>
+    partition_range(const Seq& s): base{std::begin(s), upto(std::begin(s), std::end(s))} {
+        EXPECTS(is_valid());
+    }
+
+    // explicitly check invariants
+    void validate() const {
+        auto ok = is_valid();
+        if (!ok) {
+            throw invalid_partition(ok.second());
+        }
+    }
+
+    // find half-open sub-interval containing x
+    iterator find(const inner_value_type& x) const {
+        if (empty()) {
+            return right;
+        }
+
+        auto divs = divisions();
+        auto i = std::upper_bound(divs.left, divs.right, x);
+        if (i==divs.left || i==divs.right) {
+            return right;
+        }
+        return iterator{std::prev(i)};
+    }
+
+    // access to underlying divisions
+    range<I> divisions() const {
+        return {left.get(), std::next(right.get())};
+    }
+
+    // global upper and lower bounds of partition
+    value_type bounds() const {
+        return {front().first, back().second};
+    }
+
+private:
+    either<bool, std::string> is_valid() const {
+        if (!std::is_sorted(left.get(), right.get())) {
+            return std::string("offsets are not monotonically increasing");
+        }
+        else {
+            return true;
+        }
+    }
+};
+
+
+template <
+    typename Seq,
+    typename SeqIter = typename sequence_traits<Seq>::const_iterator,
+    typename = enable_if_t<is_forward_iterator<SeqIter>::value>
+>
+partition_range<SeqIter> partition_view(const Seq& r) {
+    return partition_range<SeqIter>(r);
+}
+
+/*
+ * Construct a monotonically increasing sequence in a provided
+ * container representing a partition from a sequence of subset sizes.
+ *
+ * If the first parameter is `partition_in_place`, the provided
+ * container `divisions` will not be resized, and the partition will 
+ * be of length `util::size(divisions)-1` or zero if `divisions` is
+ * empty.
+ *
+ * Otherwise, `divisions` will be be resized to `util::size(sizes)+1`
+ * and represent a partition of length `util::size(sizes)`.
+ *
+ * Returns a partition view over `divisions`.
+ */
+
+struct partition_in_place_t {
+    constexpr partition_in_place_t() {}
+};
+
+constexpr partition_in_place_t partition_in_place;
+
+template <
+    typename Part,
+    typename Sizes,
+    typename T = typename sequence_traits<Part>::value_type
+>
+partition_range<typename sequence_traits<Part>::const_iterator>
+make_partition(partition_in_place_t, Part& divisions, const Sizes& sizes, T from=T{}) {
+    auto pi = std::begin(divisions);
+    auto pe = std::end(divisions);
+    auto si = std::begin(sizes);
+    auto se = std::end(sizes);
+
+    if (pi!=pe) {
+        *pi++ = from;
+        while (pi!=pe && si!=se) {
+            from += *si++;
+            *pi++ = from;
+        }
+        while (pi!=pe) {
+            *pi++ = from;
+        }
+    }
+    return partition_view(divisions);
+}
+
+template <
+    typename Part,
+    typename Sizes,
+    typename T = typename sequence_traits<Part>::value_type
+>
+partition_range<typename sequence_traits<Part>::const_iterator>
+make_partition(Part& divisions, const Sizes& sizes, T from=T{}) {
+    divisions.resize(size(sizes)+1);
+
+    // (would use std::inclusive_scan in C++17)
+    auto pi = std::begin(divisions);
+    for (const auto& s: sizes) {
+        *pi++ = from;
+        from += s;
+    }
+    *pi = from;
+
+    return partition_view(divisions);
+}
+
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/partition_iterator.hpp b/src/util/partition_iterator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f13b82f25d43c604680ff4e96b3efd6936e98aea
--- /dev/null
+++ b/src/util/partition_iterator.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+/*
+ * Present a monotonically increasing sequence of values given by an iterator
+ * as a sequence of pairs representing half-open intervals.
+ *
+ * Implementation is a thin wrapper over underlying iterator.
+ */
+
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include <util/iterutil.hpp>
+#include <util/meta.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+template <typename I>
+class partition_iterator: public iterator_adaptor<partition_iterator<I>, I> {
+    using base = iterator_adaptor<partition_iterator<I>, I>;
+    friend class iterator_adaptor<partition_iterator<I>, I>;
+    I inner_;
+
+    // provides access to inner iterator for adaptor.
+    const I& inner() const { return inner_; }
+    I& inner() { return inner_; }
+
+    using inner_value_type = decay_t<decltype(*inner_)>;
+
+public:
+    using typename base::difference_type;
+    using value_type = std::pair<inner_value_type, inner_value_type>;
+    using pointer = const value_type*;
+    using reference = const value_type&;
+
+    template <
+        typename J,
+        typename = enable_if_t<!std::is_same<decay_t<J>, partition_iterator>::value>
+    >
+    explicit partition_iterator(J&& c): inner_{std::forward<J>(c)} {}
+
+    // forward and input iterator requirements
+
+    value_type operator*() const {
+        return {*inner_, *std::next(inner_)};
+    }
+
+    util::pointer_proxy<value_type> operator->() const {
+        return **this;
+    }
+
+    value_type operator[](difference_type n) const {
+        return *(*this+n);
+    }
+
+    // public access to inner iterator
+    const I& get() const { return inner_; }
+};
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/range.hpp b/src/util/range.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7777444d0f65d7feebc03f8f3e988a234462cdf6
--- /dev/null
+++ b/src/util/range.hpp
@@ -0,0 +1,332 @@
+#pragma once
+
+/* Present a pair of iterators as a non-owning collection.
+ *
+ * Two public member fields, `left` and `right`, describe
+ * the half-open interval [`left`, `right`).
+ *
+ * Constness of the range object only affects mutability
+ * of the iterators, and does not relate to the constness
+ * of the data to which the iterators refer.
+ *
+ * The `right` field may differ in type from the `left` field,
+ * in which case it is regarded as a sentinel type; the end of
+ * the interval is then marked by the first successor `i` of
+ * `left` that satisfies `i==right`.
+ *
+ * For an iterator `i` and sentinel `s`, it is expected that
+ * the tests `i==s` and `i!=s` are well defined, with the
+ * corresponding semantics.
+ */
+
+#include <cstddef>
+#include <iterator>
+#include <limits>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+#ifdef WITH_TBB
+#include <tbb/tbb_stddef.h>
+#endif
+
+#include <util/counter.hpp>
+#include <util/debug.hpp>
+#include <util/either.hpp>
+#include <util/iterutil.hpp>
+#include <util/meta.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+template <typename U, typename S = U>
+struct range {
+    using iterator = U;
+    using sentinel = S;
+    using const_iterator = iterator;
+    using difference_type = typename std::iterator_traits<iterator>::difference_type;
+    using size_type = typename std::make_unsigned<difference_type>::type;
+    using value_type = typename std::iterator_traits<iterator>::value_type;
+    using reference = typename std::iterator_traits<iterator>::reference;
+    using const_reference = const value_type&;
+
+    iterator left;
+    sentinel right;
+
+    range() = default;
+    range(const range&) = default;
+    range(range&&) = default;
+
+    template <typename U1, typename U2>
+    range(U1&& l, U2&& r):
+        left(std::forward<U1>(l)), right(std::forward<U2>(r))
+    {}
+
+    range& operator=(const range&) = default;
+    range& operator=(range&&) = default;
+
+    bool empty() const { return left==right; }
+
+    iterator begin() const { return left; }
+    const_iterator cbegin() const { return left; }
+
+    sentinel end() const { return right; }
+    sentinel cend() const { return right; }
+
+    template <typename V = iterator>
+    enable_if_t<is_forward_iterator<V>::value, size_type>
+    size() const {
+        return std::distance(begin(), end());
+    }
+
+    constexpr size_type max_size() const { return std::numeric_limits<size_type>::max(); }
+
+    void swap(range& other) {
+        std::swap(left, other.left);
+        std::swap(right, other.right);
+    }
+
+    decltype(*left) front() const { return *left; }
+
+    decltype(*left) back() const { return *upto(left, right); }
+
+    template <typename V = iterator>
+    enable_if_t<is_random_access_iterator<V>::value, decltype(*left)>
+    operator[](difference_type n) const {
+        return *std::next(begin(), n);
+    }
+
+    template <typename V = iterator>
+    enable_if_t<is_random_access_iterator<V>::value, decltype(*left)>
+    at(difference_type n) const {
+        if (size_type(n) >= size()) {
+            throw std::out_of_range("out of range in range");
+        }
+        return (*this)[n];
+    }
+
+#ifdef WITH_TBB
+    template <
+        typename V = iterator,
+        typename = enable_if_t<is_forward_iterator<V>::value>
+    >
+    range(range& r, tbb::split):
+        left(r.left), right(r.right)
+    {
+        std::advance(left, r.size()/2u);
+        r.right = left;
+    }
+
+    template <
+        typename V = iterator,
+        typename = enable_if_t<is_forward_iterator<V>::value>
+    >
+    range(range& r, tbb::proportional_split p):
+        left(r.left), right(r.right)
+    {
+        size_type i = (r.size()*p.left())/(p.left()+p.right());
+        if (i<1) {
+            i = 1;
+        }
+        std::advance(left, i);
+        r.right = left;
+    }
+
+    bool is_divisible() const {
+        return is_forward_iterator<U>::value && left != right && std::next(left) != right;
+    }
+
+    static const bool is_splittable_in_proportion() {
+        return is_forward_iterator<U>::value;
+    }
+#endif
+};
+
+template <typename U, typename V>
+range<U, V> make_range(const U& left, const V& right) {
+    return range<U, V>(left, right);
+}
+
+/*
+ * Use a proxy iterator to present a range as having the same begin and
+ * end types, for use with e.g. pre-C++17 ranged-for loops or STL
+ * algorithms.
+ */
+template <typename I, typename S>
+class sentinel_iterator {
+    nest::mc::util::either<I, S> e_;
+
+    bool is_sentinel() const { return e_.index()!=0; }
+
+    I& iter() {
+        EXPECTS(!is_sentinel());
+        return e_.template unsafe_get<0>();
+    }
+
+    const I& iter() const {
+        EXPECTS(!is_sentinel());
+        return e_.template unsafe_get<0>();
+    }
+
+    S& sentinel() {
+        EXPECTS(is_sentinel());
+        return e_.template unsafe_get<1>();
+    }
+
+    const S& sentinel() const {
+        EXPECTS(is_sentinel());
+        return e_.template unsafe_get<1>();
+    }
+
+public:
+    using difference_type = typename std::iterator_traits<I>::difference_type;
+    using value_type = typename std::iterator_traits<I>::value_type;
+    using pointer = typename std::iterator_traits<I>::pointer;
+    using reference = typename std::iterator_traits<I>::reference;
+    using iterator_category = typename std::iterator_traits<I>::iterator_category;
+
+    sentinel_iterator(I i): e_(i) {}
+
+    template <typename V = S, typename = enable_if_t<!std::is_same<I, V>::value>>
+    sentinel_iterator(S i): e_(i) {}
+
+    sentinel_iterator() = default;
+    sentinel_iterator(const sentinel_iterator&) = default;
+    sentinel_iterator(sentinel_iterator&&) = default;
+
+    sentinel_iterator& operator=(const sentinel_iterator&) = default;
+    sentinel_iterator& operator=(sentinel_iterator&&) = default;
+
+    // forward and input iterator requirements
+
+    auto operator*() const -> decltype(*iter()) { return *iter(); }
+
+    I operator->() const { return e_.template ptr<0>(); }
+
+    sentinel_iterator& operator++() {
+        ++iter();
+        return *this;
+    }
+
+    sentinel_iterator operator++(int) {
+        sentinel_iterator c(*this);
+        ++*this;
+        return c;
+    }
+
+    bool operator==(const sentinel_iterator& x) const {
+        if (is_sentinel()) {
+            return x.is_sentinel() || x.iter()==sentinel();
+        }
+        else {
+            return x.is_sentinel()? iter()==x.sentinel(): iter()==x.iter();
+        }
+    }
+
+    bool operator!=(const sentinel_iterator& x) const {
+        return !(*this==x);
+    }
+
+    // bidirectional iterator requirements
+
+    sentinel_iterator& operator--() {
+        --iter();
+        return *this;
+    }
+
+    sentinel_iterator operator--(int) {
+        sentinel_iterator c(*this);
+        --*this;
+        return c;
+    }
+
+    // random access iterator requirements
+
+    sentinel_iterator &operator+=(difference_type n) {
+        iter() += n;
+        return *this;
+    }
+
+    sentinel_iterator operator+(difference_type n) const {
+        sentinel_iterator c(*this);
+        return c += n;
+    }
+
+    friend sentinel_iterator operator+(difference_type n, sentinel_iterator x) {
+        return x+n;
+    }
+
+    sentinel_iterator& operator-=(difference_type n) {
+        iter() -= n;
+        return *this;
+    }
+
+    sentinel_iterator operator-(difference_type n) const {
+        sentinel_iterator c(*this);
+        return c -= n;
+    }
+
+    difference_type operator-(sentinel_iterator x) const {
+        return iter()-x.iter();
+    }
+
+    auto operator[](difference_type n) const -> decltype(*iter()){
+        return *(iter()+n);
+    }
+
+    bool operator<=(const sentinel_iterator& x) const {
+        return x.is_sentinel() || (!is_sentinel() && iter()<=x.iter());
+    }
+
+    bool operator<(const sentinel_iterator& x) const {
+        return !is_sentinel() && (x.is_sentinel() || iter()<=x.iter());
+    }
+
+    bool operator>=(const sentinel_iterator& x) const {
+        return !(x<*this);
+    }
+
+    bool operator>(const sentinel_iterator& x) const {
+        return !(x<=*this);
+    }
+};
+
+template <typename I, typename S>
+using sentinel_iterator_t =
+    typename std::conditional<std::is_same<I, S>::value, I, sentinel_iterator<I, S>>::type;
+
+template <typename I, typename S>
+sentinel_iterator_t<I, S> make_sentinel_iterator(const I& i, const S& s) {
+    return sentinel_iterator_t<I, S>(i);
+}
+
+template <typename I, typename S>
+sentinel_iterator_t<I, S> make_sentinel_end(const I& i, const S& s) {
+    return sentinel_iterator_t<I, S>(s);
+}
+
+template <typename Seq>
+auto canonical_view(const Seq& s) ->
+    range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>>
+{
+    return {make_sentinel_iterator(std::begin(s), std::end(s)), make_sentinel_end(std::begin(s), std::end(s))};
+}
+
+/*
+ * Present a single item as a range
+ */
+
+template <typename T>
+range<T*> singleton_view(T& item) {
+    return {&item, &item+1};
+}
+
+template <typename T>
+range<const T*> singleton_view(const T& item) {
+    return {&item, &item+1};
+}
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/span.hpp b/src/util/span.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..06cdfa36924324c589af8a16412d8c167fb0429f
--- /dev/null
+++ b/src/util/span.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+/* 
+ * Presents a half-open interval [a,b) of integral values as a container.
+ */
+
+#include <type_traits>
+#include <utility>
+
+#include <util/counter.hpp>
+#include <util/range.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+template <typename I>
+using span = range<counter<I>>;
+
+template <typename I, typename J>
+span<typename std::common_type<I, J>::type> make_span(I left, J right) {
+    return span<typename std::common_type<I, J>::type>(left, right);
+}
+
+template <typename I, typename J>
+span<typename std::common_type<I, J>::type> make_span(std::pair<I, J> interval) {
+    return span<typename std::common_type<I, J>::type>(interval.first, interval.second);
+}
+
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/transform.hpp b/src/util/transform.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f7d9fed04c721f09611757c84c54e5f13cd15942
--- /dev/null
+++ b/src/util/transform.hpp
@@ -0,0 +1,109 @@
+#pragma once
+
+/*
+ * An iterator adaptor that presents the values from an underlying
+ * iterator after applying a provided functor.
+ */
+
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#include <util/iterutil.hpp>
+#include <util/meta.hpp>
+#include <util/range.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+template <typename I, typename F>
+class transform_iterator: public iterator_adaptor<transform_iterator<I, F>, I> {
+    using base = iterator_adaptor<transform_iterator<I, F>, I>;
+    friend class iterator_adaptor<transform_iterator<I, F>, I>;
+
+    I inner_;
+    F f_;
+
+    // provides access to inner iterator for adaptor.
+    const I& inner() const { return inner_; }
+    I& inner() { return inner_; }
+
+    using inner_value_type = util::decay_t<decltype(*inner_)>;
+
+public:
+    using typename base::difference_type;
+    using value_type = decltype(f_(*inner_));
+    using pointer = const value_type*;
+    using reference = const value_type&;
+
+    template <typename J, typename G>
+    transform_iterator(J&& c, G&& g): inner_{std::forward<J>(c)}, f_{std::forward<G>(g)} {}
+
+    transform_iterator(const transform_iterator&) = default;
+    transform_iterator(transform_iterator&&) = default;
+    transform_iterator& operator=(const transform_iterator&) = default;
+    transform_iterator& operator=(transform_iterator&&) = default;
+
+    // forward and input iterator requirements
+
+    value_type operator*() const {
+        return f_(*inner_);
+    }
+
+    util::pointer_proxy<value_type> operator->() const {
+        return **this;
+    }
+
+    value_type operator[](difference_type n) const {
+        return *(*this+n);
+    }
+
+    // public access to inner iterator
+    const I& get() const { return inner_; }
+
+    bool operator==(const transform_iterator& x) const { return inner_==x.inner_; }
+    bool operator!=(const transform_iterator& x) const { return inner_!=x.inner_; }
+
+    // expose inner iterator for testing against a sentinel
+    template <typename Sentinel>
+    bool operator==(const Sentinel& s) const { return inner_==s; }
+
+    template <typename Sentinel>
+    bool operator!=(const Sentinel& s) const { return !(inner_==s); }
+};
+
+template <typename I, typename F>
+transform_iterator<I, util::decay_t<F>> make_transform_iterator(const I& i, const F& f) {
+    return transform_iterator<I, util::decay_t<F>>(i, f);
+}
+
+template <
+    typename Seq,
+    typename F,
+    typename seq_citer = typename sequence_traits<Seq>::const_iterator,
+    typename seq_csent = typename sequence_traits<Seq>::const_sentinel,
+    typename = enable_if_t<std::is_same<seq_citer, seq_csent>::value>
+>
+range<transform_iterator<seq_citer, util::decay_t<F>>>
+transform_view(const Seq& s, const F& f) {
+    return {make_transform_iterator(cbegin(s), f), make_transform_iterator(cend(s), f)};
+}
+
+
+template <
+    typename Seq,
+    typename F,
+    typename seq_citer = typename sequence_traits<Seq>::const_iterator,
+    typename seq_csent = typename sequence_traits<Seq>::const_sentinel,
+    typename = enable_if_t<!std::is_same<seq_citer, seq_csent>::value>
+>
+range<transform_iterator<seq_citer, util::decay_t<F>>, seq_csent>
+transform_view(const Seq& s, const F& f) {
+    return {make_transform_iterator(cbegin(s), f), cend(s)};
+}
+
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/uninitialized.hpp b/src/util/uninitialized.hpp
index 846e46d5a2451fe1535f026ee4b801e5303a6f0c..8e3613cd6b342b40c7f2cd76edfd4e7acec5c582 100644
--- a/src/util/uninitialized.hpp
+++ b/src/util/uninitialized.hpp
@@ -18,25 +18,26 @@ namespace nest {
 namespace mc {
 namespace util {
 
-/* Maintains storage for a value of type X, with explicit
+/*
+ * Maintains storage for a value of type X, with explicit
  * construction and destruction.
  */
 template <typename X>
-struct uninitialized {
+class uninitialized {
 private:
     typename std::aligned_storage<sizeof(X), alignof(X)>::type data;
 
 public:
-    using pointer_type = X*;
-    using const_pointer_type = const X*;
-    using reference_type = X&;
-    using const_reference_type = const X&;
+    using pointer = X*;
+    using const_pointer = const X*;
+    using reference = X&;
+    using const_reference= const X&;
 
-    pointer_type ptr() { return reinterpret_cast<X*>(&data); }
-    const_pointer_type cptr() const { return reinterpret_cast<const X*>(&data); }
+    pointer ptr() { return reinterpret_cast<X*>(&data); }
+    const_pointer cptr() const { return reinterpret_cast<const X*>(&data); }
 
-    reference_type ref() { return *reinterpret_cast<X*>(&data); }
-    const_reference_type cref() const { return *reinterpret_cast<const X*>(&data); }
+    reference ref() { return *reinterpret_cast<X*>(&data); }
+    const_reference cref() const { return *reinterpret_cast<const X*>(&data); }
 
     // Copy construct the value.
     template <
@@ -60,45 +61,46 @@ public:
 
     // Apply the one-parameter functor F to the value by reference.
     template <typename F>
-    result_of_t<F(reference_type)> apply(F&& f) { return f(ref()); }
+    result_of_t<F(reference)> apply(F&& f) { return f(ref()); }
 
     // Apply the one-parameter functor F to the value by const reference.
     template <typename F>
-    result_of_t<F(const_reference_type)> apply(F&& f) const { return f(cref()); }
+    result_of_t<F(const_reference)> apply(F&& f) const { return f(cref()); }
 };
 
-/* Maintains storage for a pointer of type X, representing
+/*
+ * Maintains storage for a pointer of type X, representing
  * a possibly uninitialized reference.
  */
 template <typename X>
-struct uninitialized<X&> {
+class uninitialized<X&> {
 private:
     X *data;
 
 public:
-    using pointer_type = X*;
-    using const_pointer_type = const X*;
-    using reference_type = X&;
-    using const_reference_type = const X&;
+    using pointer = X*;
+    using const_pointer = const X*;
+    using reference = X&;
+    using const_reference = const X&;
 
-    pointer_type ptr() { return data; }
-    const_pointer_type cptr() const { return data; }
+    pointer ptr() { return data; }
+    const_pointer cptr() const { return data; }
 
-    reference_type ref() { return *data; }
-    const_reference_type cref() const { return *data; }
+    reference ref() { return *data; }
+    const_reference cref() const { return *data; }
 
     void construct(X& x) { data = &x; }
     void destruct() {}
 
     // Apply the one-parameter functor F to the value by reference.
     template <typename F>
-    result_of_t<F(reference_type)> apply(F&& f) {
+    result_of_t<F(reference)> apply(F&& f) {
         return f(ref());
     }
 
     // Apply the one-parameter functor F to the value by const reference.
     template <typename F>
-    result_of_t<F(const_reference_type)> apply(F&& f) const {
+    result_of_t<F(const_reference)> apply(F&& f) const {
         return f(cref());
     }
 };
@@ -108,17 +110,18 @@ public:
  * Allows the use of uninitialized<X> for void X, for generic applications.
  */
 template <>
-struct uninitialized<void> {
-    using pointer_type = void*;
-    using const_pointer_type = const void*;
-    using reference_type = void;
-    using const_reference_type = void;
+class uninitialized<void> {
+public:
+    using pointer = void*;
+    using const_pointer = const void*;
+    using reference = void;
+    using const_reference = void;
 
-    pointer_type ptr() { return nullptr; }
-    const_pointer_type cptr() const { return nullptr; }
+    pointer ptr() { return nullptr; }
+    const_pointer cptr() const { return nullptr; }
 
-    reference_type ref() {}
-    const_reference_type cref() const {}
+    reference ref() {}
+    const_reference cref() const {}
 
     // No operation.
     void construct(...) {}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 10fb65efd052c1d9d7d7fdd5f50bdec0f93cb5a5..c39e22a80f408f824af43f31f1d5e965b1f37ff5 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -14,6 +14,9 @@ add_subdirectory(validation)
 # Test for the internode communication (eg. mpi)
 add_subdirectory(global_communication)
 
+# Tests for performance: This could include stand alone tests. These do not necessarily be run automatically
+add_subdirectory(performance)
+
 
 # Proposed additional test types:
 
@@ -21,7 +24,6 @@ add_subdirectory(global_communication)
 
 # Test to check integration between components 
 
-# Tests for performance: This could include stand alone tests. These do not necessarily be run automatically
 
 # Numbered tests based on bugs in the tracker
 
diff --git a/tests/global_communication/CMakeLists.txt b/tests/global_communication/CMakeLists.txt
index f7d000157e4cfd130399d1f718b67fbc5eb2f86d..fc8531dac8561fa748479af8d7633e9339c9a4f4 100644
--- a/tests/global_communication/CMakeLists.txt
+++ b/tests/global_communication/CMakeLists.txt
@@ -1 +1,32 @@
-# Nothing to be done yet
+set(HEADERS
+    ${PROJECT_SOURCE_DIR}/src/swcio.hpp
+)
+set(COMMUNICATION_SOURCES
+    test_exporter_spike_file.cpp
+    # unit test driver
+    test.cpp
+)
+
+add_executable(global_communication.exe ${COMMUNICATION_SOURCES} ${HEADERS})
+
+set(TARGETS global_communication.exe)
+
+foreach(target ${TARGETS})
+    target_link_libraries(${target} LINK_PUBLIC cellalgo gtest)
+
+    if(WITH_TBB)
+        target_link_libraries(${target} LINK_PUBLIC ${TBB_LIBRARIES})
+    endif()
+
+    if(WITH_MPI)
+        target_link_libraries(${target} LINK_PUBLIC ${MPI_C_LIBRARIES})
+        set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}")
+    endif()
+
+    set_target_properties(
+        ${target}
+        PROPERTIES
+        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
+    )
+endforeach()
+
diff --git a/tests/global_communication/test.cpp b/tests/global_communication/test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c67a065bd88ed85aa56e865b1d4f8f9fa3d5677e
--- /dev/null
+++ b/tests/global_communication/test.cpp
@@ -0,0 +1,19 @@
+#include <iostream>
+#include <fstream>
+#include <numeric>
+#include <vector>
+
+#include "gtest.h"
+
+#include "../../src/communication/communicator.hpp"
+#include "../../src/communication/global_policy.hpp"
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    // We need to set the communicator policy at the top level
+    // this allows us to build multiple communicators in the tests
+    nest::mc::communication::global_policy_guard global_guard(argc, argv);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/tests/global_communication/test_exporter_spike_file.cpp b/tests/global_communication/test_exporter_spike_file.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a85b0784801df6f91c72db70b44b69b36337f2b
--- /dev/null
+++ b/tests/global_communication/test_exporter_spike_file.cpp
@@ -0,0 +1,115 @@
+#include "gtest.h"
+
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <communication/communicator.hpp>
+#include <communication/global_policy.hpp>
+#include <io/exporter_spike_file.hpp>
+
+class exporter_spike_file_fixture : public ::testing::Test {
+protected:
+    using time_type = float;
+    using communicator_type = nest::mc::communication::global_policy;
+
+    using exporter_type =
+        nest::mc::io::exporter_spike_file<time_type, communicator_type>;
+    using spike_type = exporter_type::spike_type;
+
+    std::string file_name_;
+    std::string path_;
+    std::string extension_;
+    unsigned index_;
+
+    exporter_spike_file_fixture() :
+        file_name_("spikes_exporter_spike_file_fixture"),
+        path_("./"),
+        extension_("gdf"),
+        index_(communicator_type::id())
+    {}
+
+    std::string get_standard_file_name() {
+        return exporter_type::create_output_file_path(file_name_, path_, extension_, index_);
+    }
+
+    void SetUp() {
+        // code here will execute just before the test ensues 
+    }
+
+    void TearDown() {
+        // delete the start create file
+        std::remove(get_standard_file_name().c_str());
+    }
+
+    ~exporter_spike_file_fixture()
+    {}
+};
+
+TEST_F(exporter_spike_file_fixture, constructor) {
+    exporter_type exporter(file_name_, path_, extension_, true);
+
+    //test if the file exist and depending on over_write throw or delete
+    std::ifstream f(get_standard_file_name());
+    EXPECT_TRUE(f.good());
+
+    // We now know the file exists, so create a new exporter with overwrite false
+    try {
+        exporter_type exporter1(file_name_, path_, extension_, false);
+        FAIL() << "expected a file already exists error";
+    }
+    catch (const std::runtime_error& err) {
+        EXPECT_EQ(
+            err.what(),
+            "Tried opening file for writing but it exists and over_write is false: " +
+            get_standard_file_name()
+        );
+    }
+    catch (...) {
+        FAIL() << "expected a file already exists error";
+    }
+}
+
+TEST_F(exporter_spike_file_fixture, create_output_file_path) {
+    // Create some random paths, no need for fancy tests here
+    std::string produced_filename =
+        exporter_type::create_output_file_path("spikes", "./", "gdf", 0);
+    EXPECT_STREQ(produced_filename.c_str(), "./spikes_0.gdf");
+
+    produced_filename =
+        exporter_type::create_output_file_path("a_name", "../../", "txt", 5);
+    EXPECT_STREQ(produced_filename.c_str(), "../../a_name_5.txt");
+}
+
+TEST_F(exporter_spike_file_fixture, do_export) {
+    {
+        exporter_type exporter(file_name_, path_, extension_);
+
+        // Create some spikes
+        std::vector<spike_type> spikes;
+        spikes.push_back({ { 0, 0 }, 0.0 });
+        spikes.push_back({ { 0, 0 }, 0.1 });
+        spikes.push_back({ { 1, 0 }, 1.0 });
+        spikes.push_back({ { 1, 0 }, 1.1 });
+
+        // now do the export
+        exporter.output(spikes);
+    }
+
+    // Test if we have spikes in the file?
+    std::ifstream f(get_standard_file_name());
+    EXPECT_TRUE(f.good());
+
+    std::string line;
+
+    EXPECT_TRUE(std::getline(f, line));
+    EXPECT_STREQ(line.c_str(), "0 0.0000");
+    EXPECT_TRUE(std::getline(f, line));
+    EXPECT_STREQ(line.c_str(), "0 0.1000");
+    EXPECT_TRUE(std::getline(f, line));
+    EXPECT_STREQ(line.c_str(), "1 1.0000");
+    EXPECT_TRUE(std::getline(f, line));
+    EXPECT_STREQ(line.c_str(), "1 1.1000");
+}
diff --git a/tests/performance/CMakeLists.txt b/tests/performance/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..351be926371b61bcf130d43efc42e5fb0ff53eee
--- /dev/null
+++ b/tests/performance/CMakeLists.txt
@@ -0,0 +1,2 @@
+# Unit tests
+add_subdirectory(io)
diff --git a/tests/performance/io/CMakeLists.txt b/tests/performance/io/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5bfe045ebf782c298702bd107201c9c343a2a297
--- /dev/null
+++ b/tests/performance/io/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(HEADERS
+)
+
+set(DISK_IO_SOURCES
+    disk_io.cpp
+)
+
+add_executable(disk_io.exe ${DISK_IO_SOURCES} ${HEADERS})
+
+target_link_libraries(disk_io.exe LINK_PUBLIC cellalgo)
+
+if(WITH_TBB)
+    target_link_libraries(disk_io.exe LINK_PUBLIC ${TBB_LIBRARIES})
+endif()
+
+if(WITH_MPI)
+    target_link_libraries(disk_io.exe LINK_PUBLIC ${MPI_C_LIBRARIES})
+    set_property(TARGET disk_io.exe APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}")
+endif()
+
+# Copy the python file that drives the performance tests and produces the output
+file(COPY disk_io.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
diff --git a/tests/performance/io/disk_io.cpp b/tests/performance/io/disk_io.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..526263bf395a81eb36ed050b96cfe1923e04cd25
--- /dev/null
+++ b/tests/performance/io/disk_io.cpp
@@ -0,0 +1,160 @@
+#include <stdio.h>
+
+#include <fstream>
+#include <iostream>
+#include <numeric>
+
+#include <cell.hpp>
+#include <cell_group.hpp>
+#include <common_types.hpp>
+#include <communication/communicator.hpp>
+#include <communication/global_policy.hpp>
+#include <fvm_cell.hpp>
+#include <io/exporter_spike_file.hpp>
+#include <profiling/profiler.hpp>
+
+using namespace nest::mc;
+
+using global_policy = communication::global_policy;
+using lowered_cell = fvm::fvm_cell<double, cell_local_size_type>;
+using cell_group_type = cell_group<lowered_cell>;
+using time_type = typename cell_group_type::time_type;
+using spike_type = io::exporter_spike_file<time_type, global_policy>::spike_type;
+using timer = util::timer_type;
+
+int main(int argc, char** argv) {
+
+    //Setup the possible mpi environment
+    communication::global_policy_guard global_guard(argc, argv);
+
+    // very simple command line parsing
+    if (argc < 3) {
+        std::cout << "disk_io <int nrspikes> <int nr_repeats>  [simple_output (false|true)]\n"
+                  << "   Simple performance test runner for the exporter manager\n"
+                  << "   It exports nrspikes nr_repeats using the export_manager and will produce\n"
+                  << "   the total, mean and std of the time needed to perform the output to disk\n\n"
+
+                  << "   <file_per_rank> true will produce a single file per mpi rank\n"
+                  << "   <simple_output> true will produce a simplyfied comma seperated output for automatic parsing\n\n"
+
+                  << "    The application can be started with mpi support and will produce output on a single rank\n"
+                  << "    if nrspikes is not a multiple of the nr of mpi rank, floor is take\n" ;
+        return 1;
+    }
+    auto nr_spikes = atoi(argv[1]);
+
+    if (nr_spikes == 0) {
+        std::cout << "disk_io <nrspikes>\n";
+        std::cout << "  nrspikes should be a valid integer higher then zero\n";
+
+        return 1;
+    }
+    auto nr_repeats = atoi(argv[2]);
+
+    if (nr_repeats == 0) {
+        std::cout << "disk_io <nrspikes>\n";
+        std::cout << "  nr_repeats should be a valid integer higher then zero\n";
+        return 1;
+    }
+
+    auto simple_stats = false;
+    if (argc == 4) {
+        std::string simple(argv[3]);
+        if (simple == std::string("true"))
+        {
+            simple_stats = true;
+        }
+    }
+
+    // Create the sut
+    io::exporter_spike_file<time_type, global_policy> exporter(
+         "spikes", "./", "gdf", true);
+
+    // We need the nr of ranks to calculate the nr of spikes to produce per
+    // rank
+    global_policy communication_policy;
+
+    auto nr_ranks = unsigned( communication_policy.size() );
+    auto spikes_per_rank = nr_spikes / nr_ranks;
+
+    // Create a set of spikes
+    std::vector<spike_type> spikes;
+
+    // *********************************************************************
+    // To have a  somewhat realworld data set we calculate from the nr of spikes
+    // (assuming 20 hz average) the number of nr of 'simulated' neurons,
+    // and create idxs using this value. The number of chars in the number
+    // influences the size of the output and thus the speed
+    // Also taken that we have only a single second of simulated time
+    // all spike times should be between 0.0 and 1.0:
+    auto simulated_neurons = spikes_per_rank / 20;
+    for (auto idx = unsigned{ 0 }; idx < spikes_per_rank; ++idx) {
+
+        spikes.push_back({
+            {idx % simulated_neurons, 0 },   // correct idx
+            0.0f + 1 / (0.05f + idx % 20)
+        });  // semi random float
+    }
+
+    double timings_arr[nr_repeats];
+    double time_total = 0;
+
+    // now output to disk nr_repeats times, while keeping track of the times
+    for (auto idx = 0; idx < nr_repeats; ++idx) {
+        auto time_start = timer::tic();
+        exporter.output(spikes);
+        auto run_time = timer::toc(time_start);
+
+        time_total += run_time;
+        timings_arr[idx] = run_time;
+    }
+
+    // create the vector here to prevent changes on the heap influencing the
+    // timeing
+    std::vector<double> timings;
+    for (auto idx = 0; idx < nr_repeats; ++idx) {
+        timings.push_back(timings_arr[idx]);
+    }
+
+
+    // Calculate some statistics
+    auto sum = std::accumulate(timings.begin(), timings.end(), 0.0);
+    auto mean = sum / timings.size();
+
+    std::vector<double> diff(timings.size());
+    std::transform(
+        timings.begin(), timings.end(), diff.begin(),
+        std::bind2nd(std::minus<double>(), mean)
+    );
+    auto sq_sum = std::inner_product(
+        diff.begin(), diff.end(), diff.begin(),
+        0.0
+    );
+    auto stdev = std::sqrt(sq_sum / timings.size());
+
+    auto min = *std::min_element(timings.begin(), timings.end());
+    auto max = *std::max_element(timings.begin(), timings.end());
+
+
+    if (communication_policy.id() != 0) {
+        return 0;
+    }
+
+    // and output
+    if (simple_stats) {
+        std::cout << time_total<< ","
+                  << mean  << ","
+                  << stdev << ","
+                  << min << ","
+                  << max << std::endl;
+    }
+    else {
+        std::cout << "total time (ms): " << time_total  <<  std::endl;
+        std::cout << "mean  time (ms): " << mean <<  std::endl;
+        std::cout << "stdev  time (ms): " <<  std::endl;
+        std::cout << "min  time (ms): " << min << std::endl;
+        std::cout << "max  time (ms): " << max << std::endl;
+    }
+
+    return 0;
+}
diff --git a/tests/performance/io/disk_io.py b/tests/performance/io/disk_io.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea42304b23e2da851058b8b6d7625f8185ec0db3
--- /dev/null
+++ b/tests/performance/io/disk_io.py
@@ -0,0 +1,49 @@
+import matplotlib.pyplot as plt
+import subprocess
+import os
+
+
+
+current_script_dir = os.path.dirname(os.path.abspath(__file__))
+
+spikes_to_save = 1000000
+
+print ( "Simple performance runner for spike output to file. \n" +
+        str(spikes_to_save) + " spikes will be written to a file and the duration of this \n" +
+        "operation measured for different number of ranks\n" )
+
+
+range_nr_rank = [1, 2, 4, 8, 16, 24, 32, 48, 64]
+mean = []
+std = []
+min = []
+max = []
+for n_rank in range_nr_rank:
+    # open the disk_io executable
+    p1 = subprocess.Popen(["mpirun", "-n",str(n_rank),
+                           os.path.join(current_script_dir, "disk_io.exe"),
+                           str(spikes_to_save), str(10), "true"],
+                          stdout=subprocess.PIPE)
+    
+    #and grab the raw stats
+    stats =  p1.communicate()[0]
+
+    # convert into list
+    stats = stats.split(",")
+
+    mean.append(float(stats[1]))
+    std.append(float(stats[2]))
+    min.append(float(stats[3]))
+    max.append(float(stats[4]))
+
+    print ("performed test for n_rank= " + str(n_rank))
+
+print (range_nr_rank)
+print (mean)
+print (std)
+
+plt.errorbar(range_nr_rank, mean, yerr=std, fmt='-o', label="mean (std)")
+plt.errorbar(range_nr_rank, min, fmt='-', label="min")
+plt.errorbar(range_nr_rank, max, fmt='-', label="max")
+plt.legend()
+plt.show()
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index fb1b9369d4e38498c8953fc0ce2f16c1dbc715dc..6b34d70125bb4d9792f8abb6b68daf411dfe78d8 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -14,6 +14,8 @@ set(TEST_SOURCES
     test_double_buffer.cpp
     test_cell.cpp
     test_compartments.cpp
+    test_counter.cpp
+    test_either.cpp
     test_event_queue.cpp
     test_fvm.cpp
     test_cell_group.cpp
@@ -21,17 +23,22 @@ set(TEST_SOURCES
     test_mask_stream.cpp
     test_matrix.cpp
     test_mechanisms.cpp
+    test_nop.cpp
     test_optional.cpp
     test_parameters.cpp
+    test_partition.cpp
     test_point.cpp
     test_probe.cpp
     test_segment.cpp
+    test_range.cpp
+    test_span.cpp
     test_spikes.cpp
     test_spike_store.cpp
     test_stimulus.cpp
     test_swcio.cpp
     test_synapses.cpp
     test_tree.cpp
+    test_transform.cpp
     test_uninitialized.cpp
 
     # unit test driver
diff --git a/tests/unit/test_counter.cpp b/tests/unit/test_counter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c97f2277e08a865ebb0be0a7e7b9d4db18f7360
--- /dev/null
+++ b/tests/unit/test_counter.cpp
@@ -0,0 +1,131 @@
+#include "gtest.h"
+
+#include <iterator>
+#include <type_traits>
+
+#include <util/counter.hpp>
+
+using namespace nest::mc;
+
+template <typename V>
+class counter_test: public ::testing::Test {};
+
+TYPED_TEST_CASE_P(counter_test);
+
+TYPED_TEST_P(counter_test, value) {
+    using int_type = TypeParam;
+    using counter = util::counter<int_type>;
+
+    counter c0;
+    EXPECT_EQ(int_type{0}, *c0);
+
+    counter c1{int_type{1}};
+    counter c2{int_type{2}};
+
+    EXPECT_EQ(int_type{1}, *c1);
+    EXPECT_EQ(int_type{2}, *c2);
+
+    c2 = c1;
+    EXPECT_EQ(int_type{1}, *c2);
+
+    c2 = int_type{2};
+    EXPECT_EQ(int_type{2}, *c2);
+}
+
+TYPED_TEST_P(counter_test, compare) {
+    using int_type = TypeParam;
+    using counter = util::counter<int_type>;
+
+    counter c1{int_type{1}};
+    counter c2{int_type{2}};
+
+    EXPECT_LT(c1, c2);
+    EXPECT_LE(c1, c2);
+    EXPECT_NE(c1, c2);
+    EXPECT_GE(c2, c1);
+    EXPECT_GT(c2, c1);
+
+    counter c1bis{int_type{1}};
+
+    EXPECT_LE(c1, c1bis);
+    EXPECT_EQ(c1, c1bis);
+    EXPECT_GE(c1, c1bis);
+}
+
+TYPED_TEST_P(counter_test, arithmetic) {
+    using int_type = TypeParam;
+    using counter = util::counter<int_type>;
+
+    counter c1{int_type{1}};
+    counter c2{int_type{10}};
+    int_type nine{9};
+
+    EXPECT_EQ(nine, c2-c1);
+    EXPECT_EQ(c2, c1+nine);
+    EXPECT_EQ(c2, nine+c1);
+    EXPECT_EQ(c1, c2-nine);
+
+    counter c3 = c1;
+    counter c4 = (c3 += nine);
+
+    EXPECT_EQ(c2, c3);
+    EXPECT_EQ(c3, c4);
+
+    c3 = c2;
+    c4 = (c3 -= nine);
+
+    EXPECT_EQ(c1, c3);
+    EXPECT_EQ(c3, c4);
+
+    c3 = c1;
+    EXPECT_EQ(counter{2}, ++c3);
+    EXPECT_EQ(counter{3}, ++c3);
+    EXPECT_EQ(counter{2}, --c3);
+    EXPECT_EQ(counter{1}, --c3);
+
+    c3 = c1;
+    EXPECT_EQ(counter{1}, c3++);
+    EXPECT_EQ(counter{2}, c3++);
+    EXPECT_EQ(counter{3}, c3--);
+    EXPECT_EQ(counter{2}, c3--);
+
+    EXPECT_EQ(int_type{10}, c2[0]);
+    EXPECT_EQ(int_type{4},  c2[-6]);
+    EXPECT_EQ(int_type{19}, c2[9]);
+}
+
+TYPED_TEST_P(counter_test, iterator_traits) {
+    using int_type = TypeParam;
+    using counter = util::counter<int_type>;
+    using traits = std::iterator_traits<counter>;
+
+    typename traits::reference r = *counter{int_type{3}};
+    EXPECT_EQ(r, int_type{3});
+
+    typename traits::difference_type d = counter{int_type{4}} - counter{int_type{7}};
+    EXPECT_EQ(typename traits::difference_type(-3), d);
+
+    EXPECT_TRUE((std::is_same<std::random_access_iterator_tag, typename traits::iterator_category>::value));
+}
+
+TYPED_TEST_P(counter_test, iterator_functions) {
+    using int_type = TypeParam;
+    using counter = util::counter<int_type>;
+
+    counter c1{int_type{1}};
+    counter c2{int_type{10}};
+
+    EXPECT_EQ(int_type{9}, std::distance(c1,c2));
+    counter c3{c1};
+    std::advance(c3, int_type{9});
+    EXPECT_EQ(c2, c3);
+
+    EXPECT_EQ(counter{int_type{2}}, std::next(c1));
+    EXPECT_EQ(counter{int_type{9}}, std::prev(c2));
+}
+
+REGISTER_TYPED_TEST_CASE_P(counter_test, value, compare, arithmetic, iterator_traits, iterator_functions);
+
+using int_types = ::testing::Types<signed char, unsigned char, short, unsigned short, int, unsigned, std::size_t, std::ptrdiff_t>;
+INSTANTIATE_TYPED_TEST_CASE_P(int_types, counter_test, int_types);
+
diff --git a/tests/unit/test_either.cpp b/tests/unit/test_either.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d185d8bf5eea7c7f5d0e75395d6f7e6d0f46f53
--- /dev/null
+++ b/tests/unit/test_either.cpp
@@ -0,0 +1,64 @@
+#include <typeinfo>
+#include <array>
+#include <algorithm>
+
+#include "gtest.h"
+#include "util/either.hpp"
+
+// TODO: coverage!
+
+using namespace nest::mc::util;
+
+TEST(either, basic) {
+    either<int, std::string> e0(17);
+
+    EXPECT_TRUE(e0);
+    EXPECT_EQ(17, e0.get<0>());
+    EXPECT_EQ(e0.unsafe_get<0>(), e0.get<0>());
+    EXPECT_EQ(e0.unsafe_get<0>(), e0.first());
+    EXPECT_THROW(e0.get<1>(), either_invalid_access);
+    either<int, std::string> e1("seventeen");
+
+    EXPECT_FALSE(e1);
+    EXPECT_EQ("seventeen", e1.get<1>());
+    EXPECT_EQ(e1.unsafe_get<1>(), e1.get<1>());
+    EXPECT_EQ(e1.unsafe_get<1>(), e1.second());
+    EXPECT_THROW(e1.get<0>(), either_invalid_access);
+
+    e0 = e1;
+    EXPECT_EQ("seventeen", e0.get<1>());
+    EXPECT_THROW(e0.get<0>(), either_invalid_access);
+
+    e0 = 19;
+    EXPECT_EQ(19, e0.get<0>());
+}
+
+struct no_copy {
+    int value;
+
+    no_copy(): value(23) {}
+    explicit no_copy(int v): value(v) {}
+    no_copy(const no_copy&) = delete;
+    no_copy(no_copy&&) = default;
+
+    no_copy& operator=(const no_copy&) = delete;
+    no_copy& operator=(no_copy&&) = default;
+};
+
+TEST(either, no_copy) {
+    either<no_copy, std::string> e0(no_copy{17});
+
+    EXPECT_TRUE(e0);
+
+    either<no_copy, std::string> e1(std::move(e0));
+
+    EXPECT_TRUE(e1);
+
+    either<no_copy, std::string> e2;
+    EXPECT_TRUE(e2);
+    EXPECT_EQ(23, e2.get<0>().value);
+
+    e2 = std::move(e1);
+    EXPECT_TRUE(e2);
+    EXPECT_EQ(17, e2.get<0>().value);
+}
diff --git a/tests/unit/test_nop.cpp b/tests/unit/test_nop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3571ad827555e23040658f3f7275f3bcf53d9d0
--- /dev/null
+++ b/tests/unit/test_nop.cpp
@@ -0,0 +1,75 @@
+#include "gtest.h"
+#include "util/nop.hpp"
+
+using namespace nest::mc::util;
+
+TEST(nop, void_fn) {
+    std::function<void ()> f{nop_function};
+
+    EXPECT_TRUE(f);
+    f(); // should do nothing
+
+    bool flag = false;
+    f = [&]() { flag = true; };
+    f();
+    EXPECT_TRUE(flag);
+
+    flag = false;
+    f = nop_function;
+    f();
+    EXPECT_FALSE(flag);
+
+    // with some arguments
+    std::function<void (int, int)> g{nop_function};
+    EXPECT_TRUE(g);
+    g(2, 3); // should do nothing
+
+    int sum = 0;
+    g = [&](int a, int b) { sum = a+b; };
+    g(2, 3);
+    EXPECT_EQ(5, sum);
+
+    sum = 0;
+    g = nop_function;
+    g(2, 3);
+    EXPECT_EQ(0, sum);
+}
+
+struct check_default {
+    int value = 100;
+
+    check_default() = default;
+    explicit check_default(int n): value(n) {}
+};
+
+TEST(nop, default_return_fn) {
+    std::function<check_default ()> f{nop_function};
+
+    EXPECT_TRUE(f);
+    auto result = f();
+    EXPECT_EQ(result.value, 100);
+
+    f = []() { return check_default(17); };
+    result = f();
+    EXPECT_EQ(result.value, 17);
+
+    f = nop_function;
+    result = f();
+    EXPECT_EQ(result.value, 100);
+
+    std::function<check_default (double, double)> g{nop_function};
+
+    EXPECT_TRUE(g);
+    result = g(1.4, 1.5);
+    EXPECT_EQ(result.value, 100);
+
+    g = [](double x, double y) { return check_default{(int)(x*y)}; };
+    result = g(1.4, 1.5);
+    EXPECT_EQ(result.value, 2);
+
+    g = nop_function;
+    result = g(1.4, 1.5);
+    EXPECT_EQ(result.value, 100);
+
+}
+
diff --git a/tests/unit/test_optional.cpp b/tests/unit/test_optional.cpp
index 4610f0c48cf6b317f97e8a895f9bb09245ee7220..0c7758bcf84708cc873827bad5d33cceceabbf18 100644
--- a/tests/unit/test_optional.cpp
+++ b/tests/unit/test_optional.cpp
@@ -92,6 +92,18 @@ TEST(optionalm,assign_returns) {
 
     auto bp=&(a=4);
     EXPECT_EQ(&a,bp);
+
+    auto b2=(a=optional<int>(10));
+    EXPECT_EQ(typeid(optional<int>),typeid(b2));
+
+    auto bp2=&(a=4);
+    EXPECT_EQ(&a,bp2);
+
+    auto b3=(a=nothing);
+    EXPECT_EQ(typeid(optional<int>),typeid(b3));
+
+    auto bp3=&(a=4);
+    EXPECT_EQ(&a,bp3);
 }
 
 TEST(optionalm,assign_reference) {
@@ -104,11 +116,16 @@ TEST(optionalm,assign_reference) {
     *ar = 5.0;
     EXPECT_EQ(5.0, a);
 
-    br = ar;
+    auto& check_rval=(br=ar);
     EXPECT_TRUE(br);
+    EXPECT_EQ(&br, &check_rval);
 
     *br = 7.0;
     EXPECT_EQ(7.0, a);
+
+    auto& check_rval2=(br=nothing);
+    EXPECT_FALSE(br);
+    EXPECT_EQ(&br, &check_rval2);
 }
 
 struct nomove {
@@ -212,6 +229,10 @@ TEST(optionalm,void) {
     x=b >> []() { return 1; };
     EXPECT_TRUE((bool)x);
     EXPECT_EQ(1,x.get());
+
+    auto& check_rval=(b=nothing);
+    EXPECT_FALSE((bool)b);
+    EXPECT_EQ(&b,&check_rval);
 }
 
 TEST(optionalm,bind_to_void) {
diff --git a/tests/unit/test_partition.cpp b/tests/unit/test_partition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..edfb95d1021ea452c1701d9a84ef2341ed17e48f
--- /dev/null
+++ b/tests/unit/test_partition.cpp
@@ -0,0 +1,150 @@
+#include "gtest.h"
+
+#include <array>
+#include <forward_list>
+#include <string>
+#include <vector>
+
+#include <util/debug.hpp>
+#include <util/nop.hpp>
+#include <util/partition.hpp>
+
+using namespace nest::mc;
+
+TEST(partition, partition_view) {
+    std::forward_list<int> fl = {1, 4, 6, 8, 10 };
+
+    auto p1 = util::partition_view(fl);
+    EXPECT_EQ(std::make_pair(1,4), p1.front());
+    EXPECT_EQ(std::make_pair(8,10), p1.back());
+    EXPECT_EQ(std::make_pair(1,10), p1.bounds());
+    EXPECT_EQ(4u, p1.size());
+
+    std::vector<double> v = {2.0, 3.6, 7.5};
+
+    auto p2 = util::partition_view(v);
+    EXPECT_EQ(2u, p2.size());
+
+    std::vector<double> ends;
+    std::vector<double> ends_expected = { 2.0, 3.6, 3.6, 7.5 };
+    for (auto b: p2) {
+        ends.push_back(b.first);
+        ends.push_back(b.second);
+    }
+    EXPECT_EQ(ends_expected, ends);
+}
+
+TEST(partition, short_partition_view) {
+    int two_divs[] = {10, 15};
+    EXPECT_EQ(1u, util::partition_view(two_divs).size());
+
+    int one_div[] = {10};
+    EXPECT_EQ(0u, util::partition_view(one_div).size());
+
+    std::array<int, 0> zero_divs;
+    EXPECT_EQ(0u, util::partition_view(zero_divs).size());
+}
+
+TEST(partition, check_monotonicity) {
+    // override any EXPECTS checks in partition
+    util::global_failed_assertion_handler = util::ignore_failed_assertion;
+
+    int divs_ok[] = {1, 2, 2, 3, 3};
+    EXPECT_NO_THROW(util::partition_view(divs_ok).validate());
+
+    int divs_bad[] = {3, 2, 1};
+    EXPECT_THROW(util::partition_view(divs_bad).validate(), util::invalid_partition);
+}
+
+TEST(partition, partition_view_find) {
+    std::vector<double> divs = { 1, 2.5, 3, 5.5 };
+    double eps = 0.1;
+    auto p = util::partition_view(divs);
+
+    EXPECT_EQ(p.end(), p.find(divs.front()-eps));
+    EXPECT_NE(p.end(), p.find(divs.front()));
+    EXPECT_EQ(divs.front(), p.find(divs.front())->first);
+
+    EXPECT_NE(p.end(), p.find(divs.back()-eps));
+    EXPECT_EQ(divs.back(), p.find(divs.back()-eps)->second);
+    EXPECT_EQ(p.end(), p.find(divs.back()));
+    EXPECT_EQ(p.end(), p.find(divs.back()+eps));
+
+    EXPECT_EQ(divs[1], p.find(divs[1]+eps)->first);
+    EXPECT_EQ(divs[2], p.find(divs[1]+eps)->second);
+}
+
+TEST(partition, partition_view_non_numeric) {
+    std::string divs[] = { "a", "dictionary", "of", "sorted", "words" };
+    auto p = util::partition_view(divs);
+
+    EXPECT_EQ("dictionary", p.find("elephant")->first);
+}
+
+TEST(partition, make_partition_in_place) {
+    unsigned sizes[] = { 7, 3, 0, 2 };
+    unsigned part_store[util::size(sizes)+1];
+
+    auto p = util::make_partition(util::partition_in_place, part_store, sizes, 10u);
+    ASSERT_EQ(4u, p.size());
+    EXPECT_EQ(std::make_pair(10u, 17u), p[0]);
+    EXPECT_EQ(std::make_pair(17u, 20u), p[1]);
+    EXPECT_EQ(std::make_pair(20u, 20u), p[2]);
+    EXPECT_EQ(std::make_pair(20u, 22u), p[3]);
+
+    // with short sizes sequence
+    unsigned short_sizes[] = { 1, 2 };
+    p = util::make_partition(util::partition_in_place, part_store, short_sizes, 0u);
+    ASSERT_EQ(4u, p.size());
+    EXPECT_EQ(std::make_pair(0u, 1u), p[0]);
+    EXPECT_EQ(std::make_pair(1u, 3u), p[1]);
+    EXPECT_EQ(std::make_pair(3u, 3u), p[2]);
+    EXPECT_EQ(std::make_pair(3u, 3u), p[3]);
+
+    // with longer sizes sequence
+    unsigned long_sizes[] = {1, 2, 3, 4, 5, 6};
+    p = util::make_partition(util::partition_in_place, part_store, long_sizes, 0u);
+    ASSERT_EQ(4u, p.size());
+    EXPECT_EQ(std::make_pair(0u, 1u), p[0]);
+    EXPECT_EQ(std::make_pair(1u, 3u), p[1]);
+    EXPECT_EQ(std::make_pair(3u, 6u), p[2]);
+    EXPECT_EQ(std::make_pair(6u, 10u), p[3]);
+
+    // with empty sizes sequence
+    std::array<unsigned, 0> no_sizes;
+    p = util::make_partition(util::partition_in_place, part_store, no_sizes, 17u);
+    ASSERT_EQ(4u, p.size());
+    EXPECT_EQ(std::make_pair(17u, 17u), p[0]);
+    EXPECT_EQ(std::make_pair(17u, 17u), p[1]);
+    EXPECT_EQ(std::make_pair(17u, 17u), p[2]);
+    EXPECT_EQ(std::make_pair(17u, 17u), p[3]);
+
+    // with short partition containers
+    unsigned part_store_one[1];
+    p = util::make_partition(util::partition_in_place, part_store_one, sizes, 10u);
+    ASSERT_EQ(0u, p.size());
+    ASSERT_TRUE(p.empty());
+
+    std::array<unsigned,0> part_store_zero;
+    p = util::make_partition(util::partition_in_place, part_store_zero, sizes, 10u);
+    ASSERT_EQ(0u, p.size());
+    ASSERT_TRUE(p.empty());
+}
+
+TEST(partition, make_partition) {
+    // (also tests differing types for sizes and divisiosn)
+    unsigned sizes[] = { 7, 3, 0, 2 };
+    std::forward_list<double> part_store = { 100.3 };
+
+    auto p = util::make_partition(part_store, sizes, 10.0);
+    ASSERT_EQ(4u, p.size());
+
+    auto pi = p.begin();
+    EXPECT_EQ(10.0, pi++->first);
+    EXPECT_EQ(17.0, pi++->first);
+    EXPECT_EQ(20.0, pi++->first);
+    EXPECT_EQ(20.0, pi->first);
+    EXPECT_EQ(22.0, pi->second);
+
+    EXPECT_EQ(p.end(), ++pi);
+}
diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7847212e3f0b0546c1f974dbdebab3303a15ea1d
--- /dev/null
+++ b/tests/unit/test_range.cpp
@@ -0,0 +1,171 @@
+#include "gtest.h"
+
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <list>
+#include <numeric>
+#include <type_traits>
+
+#ifdef WITH_TBB
+#include <tbb/tbb_stddef.h>
+#endif
+
+#include <util/range.hpp>
+
+using namespace nest::mc;
+
+TEST(range, list_iterator) {
+    std::list<int> l = { 2, 4, 6, 8, 10 };
+
+    auto s = util::make_range(l.begin(), l.end());
+
+    EXPECT_EQ(s.left, l.begin());
+    EXPECT_EQ(s.right, l.end());
+
+    EXPECT_EQ(s.begin(), l.begin());
+    EXPECT_EQ(s.end(), l.end());
+
+    EXPECT_EQ(s.size(), l.size());
+    EXPECT_EQ(s.front(), *l.begin());
+    EXPECT_EQ(s.back(), *std::prev(l.end()));
+
+    int check = std::accumulate(l.begin(), l.end(), 0);
+    int sum = 0;
+    for (auto i: s) {
+        sum += i;
+    }
+
+    EXPECT_EQ(check, sum);
+
+    auto sum2 = std::accumulate(s.begin(), s.end(), 0);
+    EXPECT_EQ(check, sum2);
+}
+
+TEST(range, pointer) {
+    int xs[] = { 10, 11, 12, 13, 14, 15, 16 };
+    int l = 2;
+    int r = 5;
+
+    util::range<int *> s(&xs[l], &xs[r]);
+    auto s_deduced = util::make_range(xs+l, xs+r);
+
+    EXPECT_TRUE((std::is_same<decltype(s), decltype(s_deduced)>::value));
+    EXPECT_EQ(s.left, s_deduced.left);
+    EXPECT_EQ(s.right, s_deduced.right);
+
+    EXPECT_EQ(3u, s.size());
+
+    EXPECT_EQ(xs[l], *s.left);
+    EXPECT_EQ(xs[l], *s.begin());
+    EXPECT_EQ(xs[l], s[0]);
+    EXPECT_EQ(xs[l], s.front());
+
+    EXPECT_EQ(xs[r], *s.right);
+    EXPECT_EQ(xs[r], *s.end());
+    EXPECT_THROW(s.at(r-l), std::out_of_range);
+
+    EXPECT_EQ(r-l, std::distance(s.begin(), s.end()));
+
+    EXPECT_TRUE(std::equal(s.begin(), s.end(), &xs[l]));
+}
+
+TEST(range, input_iterator) {
+    int nums[] = { 10, 9, 8, 7, 6 };
+    std::istringstream sin("10 9 8 7 6");
+    auto s = util::make_range(std::istream_iterator<int>(sin), std::istream_iterator<int>());
+
+    EXPECT_TRUE(std::equal(s.begin(), s.end(), &nums[0]));
+}
+
+TEST(range, const_iterator) {
+    std::vector<int> xs = { 1, 2, 3, 4, 5 };
+    auto r = util::make_range(xs.begin(), xs.end());
+    EXPECT_TRUE((std::is_same<int&, decltype(r.front())>::value));
+
+    const auto& xs_const = xs;
+    auto r_const = util::make_range(xs_const.begin(), xs_const.end());
+    EXPECT_TRUE((std::is_same<const int&, decltype(r_const.front())>::value));
+}
+
+struct null_terminated_t {
+    bool operator==(const char *p) const { return !*p; }
+    bool operator!=(const char *p) const { return !!*p; }
+
+    friend bool operator==(const char *p, null_terminated_t x) {
+        return x==p;
+    }
+
+    friend bool operator!=(const char *p, null_terminated_t x) {
+        return x!=p;
+    }
+
+    constexpr null_terminated_t() {}
+};
+
+constexpr null_terminated_t null_terminated;
+
+TEST(range, sentinel) {
+    const char *cstr = "hello world";
+    std::string s;
+
+    auto cstr_range = util::make_range(cstr, null_terminated);
+    for (auto i=cstr_range.begin(); i!=cstr_range.end(); ++i) {
+        s += *i;
+    }
+
+    EXPECT_EQ(s, std::string(cstr));
+
+    s.clear();
+    for (auto c: canonical_view(cstr_range)) {
+        s += c;
+    }
+
+    EXPECT_EQ(s, std::string(cstr));
+}
+
+#ifdef WITH_TBB
+
+TEST(range, tbb_split) {
+    constexpr std::size_t N = 20;
+    int xs[N];
+
+    for (unsigned i = 0; i<N; ++i) {
+        xs[i] = i;
+    }
+
+    auto s = util::make_range(&xs[0], &xs[0]+N);
+
+    while (s.size()>1) {
+        auto ssize = s.size();
+        auto r = decltype(s){s, tbb::split{}};
+        EXPECT_GT(r.size(), 0u);
+        EXPECT_GT(s.size(), 0u);
+        EXPECT_EQ(ssize, r.size()+s.size());
+        EXPECT_EQ(s.end(), r.begin());
+
+        EXPECT_TRUE(r.size()>1 || !r.is_divisible());
+        EXPECT_TRUE(s.size()>1 || !s.is_divisible());
+    }
+
+    for (unsigned i = 1; i<N-1; ++i) {
+        s = util::make_range(&xs[0], &xs[0]+N);
+        // expect exact splitting by proportion in this instance
+
+        auto r = decltype(s){s, tbb::proportional_split{i, N-i}};
+        EXPECT_EQ(&xs[0], s.left);
+        EXPECT_EQ(&xs[0]+i, s.right);
+        EXPECT_EQ(&xs[0]+i, r.left);
+        EXPECT_EQ(&xs[0]+N, r.right);
+    }
+}
+
+TEST(range, tbb_no_split) {
+    std::istringstream sin("10 9 8 7 6");
+    auto s = util::make_range(std::istream_iterator<int>(sin), std::istream_iterator<int>());
+
+    EXPECT_FALSE(decltype(s)::is_splittable_in_proportion());
+    EXPECT_FALSE(s.is_divisible());
+}
+
+#endif
diff --git a/tests/unit/test_span.cpp b/tests/unit/test_span.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..edb55f7d65e7f0321add82de6e0879143050fede
--- /dev/null
+++ b/tests/unit/test_span.cpp
@@ -0,0 +1,93 @@
+#include "gtest.h"
+
+#include <algorithm>
+#include <iterator>
+#include <list>
+#include <numeric>
+#include <type_traits>
+#include <utility>
+
+#include <util/span.hpp>
+
+using namespace nest::mc;
+
+TEST(span, int_access) {
+    using span = util::span<int>;
+
+    int n = 97;
+    int a = 3;
+    int b = a+n;
+
+    span s(a, b);
+    EXPECT_EQ(s.left, a);
+    EXPECT_EQ(s.right, b);
+
+    EXPECT_EQ(s.size(), std::size_t(n));
+
+    EXPECT_EQ(s.front(), a);
+    EXPECT_EQ(s.back(), b-1);
+
+    EXPECT_EQ(s[0], a);
+    EXPECT_EQ(s[1], a+1);
+    EXPECT_EQ(s[n-1], b-1);
+
+    EXPECT_NO_THROW(s.at(0));
+    EXPECT_NO_THROW(s.at(n-1));
+    EXPECT_THROW(s.at(n), std::out_of_range);
+    EXPECT_THROW(s.at(n+1), std::out_of_range);
+    EXPECT_THROW(s.at(-1), std::out_of_range);
+}
+
+TEST(span, int_iterators) {
+    using span = util::span<int>;
+
+    int n = 97;
+    int a = 3;
+    int b = a+n;
+
+    span s(a, b);
+
+    EXPECT_TRUE(util::is_iterator<span::iterator>::value);
+    EXPECT_TRUE(util::is_random_access_iterator<span::iterator>::value);
+
+    EXPECT_EQ(n, std::distance(s.begin(), s.end()));
+    EXPECT_EQ(n, std::distance(s.cbegin(), s.cend()));
+
+    int sum = 0;
+    for (auto i: span(a, b)) {
+        sum += i;
+    }
+    EXPECT_EQ(sum, (a+b-1)*(b-a)/2);
+}
+
+TEST(span, make_span) {
+    auto s_empty = util::make_span(3, 3);
+    EXPECT_TRUE(s_empty.empty());
+
+    {
+        auto s = util::make_span((short)3, (unsigned long long)10);
+        auto first = s.front();
+        auto last = s.back();
+
+        EXPECT_EQ(3u, first);
+        EXPECT_EQ(9u, last);
+
+        EXPECT_TRUE((std::is_same<decltype(first), decltype(last)>::value));
+        EXPECT_TRUE((std::is_same<unsigned long long, decltype(first)>::value));
+    }
+
+    {
+        // type abuse! should promote bool to long in span.
+        std::pair<long, bool> bounds(-3, false);
+        auto s = util::make_span(bounds);
+        auto first = s.front();
+        auto last = s.back();
+
+        EXPECT_EQ(-3, first);
+        EXPECT_EQ(-1, last);
+
+        EXPECT_TRUE((std::is_same<decltype(first), decltype(last)>::value));
+        EXPECT_TRUE((std::is_same<long, decltype(first)>::value));
+    }
+}
+
diff --git a/tests/unit/test_transform.cpp b/tests/unit/test_transform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9212293fbf7a59674eb81f3ccac1638b1df04077
--- /dev/null
+++ b/tests/unit/test_transform.cpp
@@ -0,0 +1,56 @@
+#include "gtest.h"
+
+#include <cctype>
+#include <forward_list>
+#include <vector>
+
+#include <util/range.hpp>
+#include <util/transform.hpp>
+
+using namespace nest::mc;
+
+TEST(transform, transform_view) {
+    std::forward_list<int> fl = {1, 4, 6, 8, 10 };
+    std::vector<double> result;
+
+    auto r = util::transform_view(fl, [](int i) { return i*i+0.5; });
+
+    EXPECT_EQ(5u, util::size(r));
+    EXPECT_EQ(16.5, *(std::next(std::begin(r), 1)));
+
+    std::copy(r.begin(), r.end(), std::back_inserter(result));
+    std::vector<double> expected = { 1.5, 16.5, 36.5, 64.5, 100.5 };
+
+    EXPECT_EQ(expected, result);
+}
+
+struct null_terminated_t {
+    bool operator==(const char *p) const { return !*p; }
+    bool operator!=(const char *p) const { return !!*p; }
+
+    friend bool operator==(const char *p, null_terminated_t x) {
+        return x==p;
+    }
+
+    friend bool operator!=(const char *p, null_terminated_t x) {
+        return x!=p;
+    }
+
+    constexpr null_terminated_t() {}
+};
+
+constexpr null_terminated_t null_terminated;
+
+char upper(char c) { return std::toupper(c); }
+
+TEST(transform, transform_view_sentinel) {
+    const char *hello = "hello";
+    auto r = util::transform_view(util::make_range(hello, null_terminated), upper);
+
+    std::string out;
+    for (auto i = r.begin(); i!=r.end(); ++i) {
+        out += *i;
+    }
+    EXPECT_EQ("HELLO", out);
+}
+
diff --git a/tests/validation/CMakeLists.txt b/tests/validation/CMakeLists.txt
index c6c72fa04efab7394190288ba3face603d141faf..ae892560492dccca331036ad1481d2f4ce5da7bb 100644
--- a/tests/validation/CMakeLists.txt
+++ b/tests/validation/CMakeLists.txt
@@ -19,19 +19,20 @@ set(TARGETS validate.exe)
 
 foreach(target ${TARGETS})
     target_link_libraries(${target} LINK_PUBLIC cellalgo gtest)
-    
+
     if(WITH_TBB)
-	target_link_libraries(${target} LINK_PUBLIC ${TBB_LIBRARIES})
+        target_link_libraries(${target} LINK_PUBLIC ${TBB_LIBRARIES})
     endif()
 
     if(WITH_MPI)
-	target_link_libraries(${target} LINK_PUBLIC ${MPI_C_LIBRARIES})
-	set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}")
+        target_link_libraries(${target} LINK_PUBLIC ${MPI_C_LIBRARIES})
+        set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${MPI_C_LINK_FLAGS}")
     endif()
 
-    set_target_properties(${target}
-       PROPERTIES
-       RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
+    set_target_properties(
+        ${target}
+        PROPERTIES
+        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
     )
 endforeach()