diff --git a/miniapp/io.cpp b/miniapp/io.cpp
index 46498fd260c2d47a823aab94c0adb1ad4e2d51ba..6b81a62c853fbc2098d343c0c61b926903f47c52 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"
@@ -21,9 +24,8 @@ 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;
@@ -36,101 +38,221 @@ 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,
+    // Default options:
+    const cl_options defopts{1000, 500, "expsyn", 100, 100., 0.025, false,
                              false, 1.0, "trace_", util::nothing};
 
     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);
 
+        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");
+                }
+                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);
+
+        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;
 }
 
@@ -150,7 +272,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..510a0611c9e72c4084cf3487579a1a5b239efadc 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;
diff --git a/src/util/optional.hpp b/src/util/optional.hpp
index 6b4d0f8b66cbdcf24c59914466a01621895ca2bb..01e055c1730812eb04c7b329abc80a71a21f075c 100644
--- a/src/util/optional.hpp
+++ b/src/util/optional.hpp
@@ -268,6 +268,11 @@ struct optional: detail::optional_base<X> {
     template <typename T>
     optional(optional<T>&& ot): base(ot.set, std::move(ot.ref())) {}
 
+    optional& operator=(nothing_t) {
+        reset();
+        return *this;
+    }
+
     template <
         typename Y,
         typename = detail::enable_unless_optional_t<Y>
@@ -331,6 +336,7 @@ 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() {}
@@ -339,6 +345,11 @@ struct optional<X&>: detail::optional_base<X&> {
     template <typename T>
     optional(optional<T&>& ot): base(ot.set,ot.ref()) {}
 
+    optional& operator=(nothing_t) {
+        reset();
+        return *this;
+    }
+
     template <typename Y>
     optional& operator=(Y& y) {
         set = true;
@@ -364,6 +375,7 @@ template <>
 struct optional<void>: detail::optional_base<void> {
     using base = detail::optional_base<void>;
     using base::set;
+    using base::reset;
 
     optional(): base() {}
 
@@ -373,6 +385,11 @@ struct optional<void>: detail::optional_base<void> {
     template <typename T>
     optional(const optional<T>& o): base(o.set,true) {}
 
+    optional& operator=(nothing_t) {
+        reset();
+        return *this;
+    }
+
     template <typename T>
     optional& operator=(T) {
         set = true;
@@ -394,7 +411,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<
@@ -408,7 +425,7 @@ 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>>
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) {