[med-svn] [cnrun] 01/05: implement NML export

andrei zavada hmmr-guest at moszumanska.debian.org
Wed Dec 28 17:57:00 UTC 2016


This is an automated email from the git hooks/post-receive script.

hmmr-guest pushed a commit to branch WIP
in repository cnrun.

commit b2837ebc10cd07be444f729982ac45d8c9d45939
Author: Andrei Zavada <johnhommer at gmail.com>
Date:   Tue Dec 27 05:57:59 2016 +0200

    implement NML export
---
 upstream/ChangeLog                                |   4 +
 upstream/Makefile.am                              |   1 +
 upstream/configure.ac                             |   8 +-
 upstream/doc/examples/example1.lua                | 117 ++++++++++--
 upstream/doc/lua-api/cnrun-lua-api.texi           |   2 +-
 upstream/src/libcnrun/model/cycle.cc              |   5 +-
 upstream/src/libcnrun/model/model.hh              |  19 +-
 upstream/src/libcnrun/model/nmlio.cc              | 211 ++++++++++++++++------
 upstream/src/libcnrun/model/struct.cc             |  57 +++---
 upstream/src/libcnrun/units/base-neuron.hh        |  10 +-
 upstream/src/libcnrun/units/base-synapse.hh       |   3 +-
 upstream/src/libcnrun/units/base-unit.cc          | 102 +++++++++--
 upstream/src/libcnrun/units/base-unit.hh          |  47 +++--
 upstream/src/libcnrun/units/hosted-neurons.cc     |   8 +-
 upstream/src/libcnrun/units/hosted-neurons.hh     |  84 ++++-----
 upstream/src/libcnrun/units/standalone-neurons.cc |  20 +-
 upstream/src/libcnrun/units/standalone-neurons.hh |  53 +++---
 upstream/src/lua-cnrun/commands.cc                |   4 +-
 upstream/src/tools/hh-latency-estimator.cc        |  18 +-
 19 files changed, 507 insertions(+), 266 deletions(-)

diff --git a/upstream/ChangeLog b/upstream/ChangeLog
index 6f20a86..7f5fe8f 100644
--- a/upstream/ChangeLog
+++ b/upstream/ChangeLog
@@ -1,3 +1,7 @@
+2016-12-25  andrei zavada  <johnhommer at gmail.com>
+	* Implement NML export.
+	* Release 2.1.
+
 2016-01-12  andrei zavada  <johnhommer at gmail.com>
 	* Fix decimate and putout.
 	* Release 2.0.3.
diff --git a/upstream/Makefile.am b/upstream/Makefile.am
index 98cfaed..4789435 100644
--- a/upstream/Makefile.am
+++ b/upstream/Makefile.am
@@ -33,6 +33,7 @@ unwanted_files = \
 	m4/ltsugar.m4 \
 	m4/ltversion.m4 \
 	aclocal.m4 \
+	compile \
 	config.sub \
 	ltmain.sh \
 	depcomp \
diff --git a/upstream/configure.ac b/upstream/configure.ac
index d945eb0..b335672 100644
--- a/upstream/configure.ac
+++ b/upstream/configure.ac
@@ -1,6 +1,6 @@
 AC_COPYRIGHT([Copyright (c) 2008-15 Andrei Zavada <johnhommer at gmail.com>])
 
-AC_INIT([cnrun], [2.0.3], [johnhommer at gmail.com])
+AC_INIT([cnrun], [2.1.0], [johnhommer at gmail.com])
 AC_CONFIG_SRCDIR([src/libcnrun/model/model.hh])
 AC_CONFIG_MACRO_DIR([m4])
 AC_PREREQ(2.61)
@@ -65,14 +65,12 @@ AX_LUA_HEADERS2014
 dnl we cannot do strcmp in cpp, so here's bash to the rescue
 if test x"$LUA_VERSION" = x"5.2"; then
    AC_DEFINE([HAVE_LUA_52], [], ["Do we have lua 5.2?"])
-else
-   AC_DEFINE([HAVE_LUA_51], [], ["Do we have lua 5.1?"])
 fi
 
 AC_ARG_ENABLE(
 	[tools],
-	AS_HELP_STRING( [--enable-tools], [build spike2sdf, varfold & hh-latency-estimator (default = no)]),
-	[do_tools=$enableval], [do_tools=no])
+	AS_HELP_STRING( [--enable-tools], [build spike2sdf, varfold & hh-latency-estimator (default = yes)]),
+	[do_tools=$enableval], [do_tools=yes])
 AM_CONDITIONAL(DO_TOOLS, [test x"$do_tools" = xyes])
 
 AC_ARG_ENABLE(
diff --git a/upstream/doc/examples/example1.lua b/upstream/doc/examples/example1.lua
index 3f91c82..bf4900c 100644
--- a/upstream/doc/examples/example1.lua
+++ b/upstream/doc/examples/example1.lua
@@ -35,6 +35,55 @@ if res == nil then
 end
 C = ult
 
+function print_units (model_name, ult, unit_regex)
+   print ("There are " .. #ult .. " unit(s) matching \"" .. unit_regex .. "\"")
+   local unit_list = ult
+   local fmt = " %-10s %-16s %-16s %-12s %-16s %-6s"
+   print (string.format(
+             fmt,
+             "label", "class", "family", "species", "has_sources", "is_altered"))
+   print (string.rep('-', 87))
+   for _, u in ipairs(unit_list) do
+      result = {M.get_unit_properties (C, model_name, u)}
+      res, ult = result[1], {effective_unpack(result, 2)}
+      local b = function (x) if x then return "yes" else return "no" end end
+      print (string.format(
+                fmt,
+                ult[1], ult[2], ult[3], ult[4], b(ult[5]), b(ult[6])))
+   end
+   print()
+end
+
+function compare_models (model_name1, model_name2)
+   local result1 = {M.get_units_matching(C, model_name1, ".*")}
+   local result2 = {M.get_units_matching(C, model_name2, ".*")}
+   res, ult1 = result[1], {effective_unpack(result1, 2)}
+   if res == nil then
+      print (ult1)
+      return
+   end
+   res, ult2 = result[1], {effective_unpack(result2, 2)}
+   if res == nil then
+      print (ult2)
+      return
+   end
+   local relevant_list1 = get_relevant_unit_params (model_name1, ult1)
+   local relevant_list2 = get_relevant_unit_params (model_name2, ult2)
+
+   return relevant_list1 == relevant_list2
+end
+
+function get_relevant_unit_params (model_name, unit_list)
+   local rup = {}
+   for _, u in ipairs(unit_list) do
+      result = {M.get_unit_properties (C, model_name, u)}
+      res, ult = result[1], {effective_unpack(result, 2)}
+      table.insert (rup, {ult[1], ult[2], ult[3], ult[4], ult[5], ult[6]})
+   end
+   return rup
+end
+
+
 local mname = "FAFA"
 res, ult = M.new_model (C, mname)
 if res == nil then
@@ -44,8 +93,9 @@ end
 model = ult
 print ("Created model")
 
-print ("Setting verbosely to 4")
-M.set_model_parameter (C, mname, "verbosely", 4)
+local verbosely = 3
+print ("Setting verbosely to " .. verbosely)
+M.set_model_parameter (C, mname, "verbosely", verbosely)
 
 result = {M.list_models (C)}
 res, ult = result[1], {effective_unpack(result, 2)}
@@ -69,6 +119,46 @@ end
 print ()
 
 
+print ("Testing NML export")
+local mname2 = "FAFA2"
+local f2 = "/tmp/m2.nml"
+
+print ("Exporting to " .. f2)
+res, ult = M.export_nml (C, mname, f2)
+if res == nil then
+   print (ult)
+   return
+end
+
+M.new_model (C, mname2)
+print ("Importing into new model from " .. f2)
+res, ult = M.import_nml (C, mname2, f2)
+if res == nil then
+   print (ult)
+   return
+end
+
+print ()
+print ("Comparing old and new models")
+if compare_models (mname, mname2) then
+   print ()
+   print ("Models appear to be different:")
+   print ("Original model:")
+   print_units (mname, ult1, ".*")
+   print ("Reimported model:")
+   print_units (mname2, ult2, ".*")
+   return
+end
+print ("Models are pretty much the same")
+print()
+
+M.delete_model (C, mname2)
+if res == nil then
+   print (ult)
+   return
+end
+
+
 print ("Host parmeters:")
 local parameters = {
    "verbosely", "integration_dt_min",
@@ -96,23 +186,8 @@ if res == nil then
    print (ult)
    return
 end
-print ()
-print ("There are " .. #ult .. " unit(s) matching L.*:")
-local unit_list = ult
-local fmt = " %-10s %-16s %-16s %-12s %-16s %-6s"
-print (string.format(
-          fmt,
-          "label", "class", "family", "species", "has_sources", "is_altered"))
-print (string.rep('-', 87))
-for _, u in ipairs(unit_list) do
-   result = {M.get_unit_properties (C, mname, u)}
-   res, ult = result[1], {effective_unpack(result, 2)}
-   local b = function (x) if x then return "yes" else return "no" end end
-   print (string.format(
-             fmt,
-             ult[1], ult[2], ult[3], ult[4], b(ult[5]), b(ult[6])))
-end
-print()
+
+print_units (mname, ult, "L.*")
 
 
 print ("Advancing 10 sec:")
@@ -166,7 +241,9 @@ print()
 
 local affected, remaining
 print ("Putout:")
--- there is unit_list already:
+result = {M.get_units_matching(C, mname, ".*")}
+res, ult = result[1], {effective_unpack(result, 2)}
+local unit_list = ult
 math.randomseed(os.time())
 local deleting = unit_list[math.random(1, #unit_list)]
 -- deleting, _ = string.gsub(deleting, ".", "\\.")
diff --git a/upstream/doc/lua-api/cnrun-lua-api.texi b/upstream/doc/lua-api/cnrun-lua-api.texi
index 6e76736..738c3a9 100644
--- a/upstream/doc/lua-api/cnrun-lua-api.texi
+++ b/upstream/doc/lua-api/cnrun-lua-api.texi
@@ -177,7 +177,7 @@ Models can be populated by constituent neurons and synapses in two ways:
 
 @defun new_neuron (C, M, type, label)
   Create a neuron of type @var{type}, with this @var{label}, in model
-  @var{M}.
+  @var{M}.  @var{label} must be of the form ``population.id''.
 @end defun
 
 @defun new_synapse (C, M, type, source, target)
diff --git a/upstream/src/libcnrun/model/cycle.cc b/upstream/src/libcnrun/model/cycle.cc
index 5247bae..ca5379a 100644
--- a/upstream/src/libcnrun/model/cycle.cc
+++ b/upstream/src/libcnrun/model/cycle.cc
@@ -280,10 +280,7 @@ prepare_advance()
                      N->n_spikes_in_last_dt() &&                        \
                      options.log_spikers ) {                            \
                         (*_spike_logger) << model_time() << "\t";       \
-                        if ( options.log_spikers_use_serial_id )        \
-                                (*_spike_logger) << N->_serial_id << endl; \
-                        else                                            \
-                                (*_spike_logger) << N->_label << endl;  \
+                        (*_spike_logger) << N->_label << endl;          \
                 }                                                       \
         }
 
diff --git a/upstream/src/libcnrun/model/model.hh b/upstream/src/libcnrun/model/model.hh
index 88c2c3f..6173bb2 100644
--- a/upstream/src/libcnrun/model/model.hh
+++ b/upstream/src/libcnrun/model/model.hh
@@ -118,7 +118,7 @@ class CModel : public cnrun::stilton::C_verprintf {
         C_BaseUnit    *unit_by_label( const string&) const;
         C_BaseNeuron  *neuron_by_label( const string&) const;
         C_BaseSynapse *synapse_by_label( const string&) const;
-        unsigned short longest_label() const
+        size_t longest_label() const
                 {  return _longest_label;  }
 
       // Unit tally
@@ -158,17 +158,17 @@ class CModel : public cnrun::stilton::C_verprintf {
         int include_unit( C_StandaloneSynapse*);
 
         C_BaseNeuron*
-        add_neuron_species( TUnitType, const string& label,
+        add_neuron_species( TUnitType, const char *pop, const char* id,
                             TIncludeOption = TIncludeOption::is_last,
                             double x = 0., double y = 0., double z = 0.);
         C_BaseNeuron*
-        add_neuron_species( const string& type, const string& label,
+        add_neuron_species( const char *type, const char *pop, const char* id,
                             TIncludeOption = TIncludeOption::is_last,
                             double x = 0., double y = 0., double z = 0.);
 
         enum class TSynapseCloningOption { yes, no, };
         C_BaseSynapse*
-        add_synapse_species( const string& type, const string& src, const string& tgt,
+        add_synapse_species( const char *type, const char *src, const char *tgt,
                              double g,
                              TSynapseCloningOption = TSynapseCloningOption::yes,
                              TIncludeOption = TIncludeOption::is_last);
@@ -187,6 +187,11 @@ class CModel : public cnrun::stilton::C_verprintf {
         void delete_unit( C_BaseUnit* u)
                 {  exclude_unit( u, TExcludeOption::with_delete);  }
 
+        void update_longest_label(size_t label_size)
+                {
+                        _longest_label = max(_longest_label, label_size);
+                }
+
       // 1. NeuroMl interface
         enum class TNMLImportOption { merge, reset, };
         enum TNMLIOResult {
@@ -448,9 +453,6 @@ class CModel : public cnrun::stilton::C_verprintf {
         list<unsigned>
                 regular_periods_last_checked;
 
-        unsigned long
-                _global_unit_id_reservoir;
-
       // the essential mechanical parts: ----
       // hosted unit variables
         vector<double> V,        // contains catenated var vectors of all constituent neurons and synapses
@@ -477,8 +479,7 @@ class CModel : public cnrun::stilton::C_verprintf {
                 is_diskless:1,
                 have_ddtb_units:1;
 
-        unsigned short
-                _longest_label;
+        size_t  _longest_label;
 
         gsl_rng *_rng;
 
diff --git a/upstream/src/libcnrun/model/nmlio.cc b/upstream/src/libcnrun/model/nmlio.cc
index 9711377..8221a72 100644
--- a/upstream/src/libcnrun/model/nmlio.cc
+++ b/upstream/src/libcnrun/model/nmlio.cc
@@ -22,6 +22,7 @@
 
 
 using namespace std;
+using cnrun::stilton::str::sasprintf;
 
 int
 cnrun::CModel::
@@ -188,19 +189,18 @@ _process_populations( xmlNode *n)
                         }
 
                         xmlNode *nin = n->children;  // again, ->children means ->first
-                        if ( nin )
-                                for ( ; nin; nin = nin->next )  // deal with multiple <instances> nodes
-                                        if ( nin->type == XML_ELEMENT_NODE && xmlStrEqual( nin->name, BAD_CAST "instances") ) {
-                                                int subretval = _process_population_instances(
-                                                        nin->children,
-                                                        group_id_s, cell_type_s);
-                                                if ( subretval < 0 )
-                                                        throw subretval;
-
-                                                vp( 2, " %5d instance(s) of type \"%s\" in population \"%s\"\n",
-                                                    subretval, cell_type_s,  group_id_s);
-                                                ++pop_cnt;
-                                        }
+                        for ( ; nin; nin = nin->next )  // deal with multiple <instances> nodes
+                                if ( nin->type == XML_ELEMENT_NODE && xmlStrEqual( nin->name, BAD_CAST "instances") ) {
+                                        int subretval = _process_population_instances(
+                                                nin->children,
+                                                group_id_s, cell_type_s);
+                                        if ( subretval < 0 )
+                                                throw subretval;
+
+                                        vp( 2, " %5d instance(s) of type \"%s\" in population \"%s\"\n",
+                                            subretval, cell_type_s,  group_id_s);
+                                        ++pop_cnt;
+                                }
 
                         xmlFree( cell_type_s), xmlFree( group_id_s);
                 }
@@ -305,17 +305,17 @@ int
 cnrun::CModel::
 _process_population_instances(
         xmlNode *n,
-        const xmlChar *group_prefix,
+        const xmlChar *population,
         const xmlChar *type_s)
 {
         int     retval = 0;  // also keeps a count of added neurons
 
         double  x, y, z;
-        char    cell_id[C_BaseUnit::max_label_size];
+        string  label;
 
         xmlNode *nin;
 
-        xmlChar *id_s = nullptr;
+        char    *id_s = nullptr;
         try {
                 for ( ; n; n = n->next ) {
                         if ( n->type != XML_ELEMENT_NODE || !xmlStrEqual( n->name, BAD_CAST "instance") )
@@ -323,29 +323,10 @@ _process_population_instances(
 
                         xmlChar *id_s = xmlGetProp( n, BAD_CAST "id");
                         if ( !id_s ) {
-                              // could be less strict here and allow empty ids, which will then be composed
-                              // from group_prefix + id (say, "LN0", "LN1" and so on); but then, as
-                              // individual <projection>s would have to reference both endpoints by explicit
-                              // ids, it is obviously prone to error to have <instance> ids depend solely on
-                              // their order of appearance.
-                              // So we bark at empty ids.
                                 fprintf( stderr, "<instance> element without an \"id\" attribute near line %u\n", n->line);
                                 return TNMLIOResult::badattr;
                         }
 
-                        size_t total_len = xmlStrlen( group_prefix) + xmlStrlen( id_s);
-                        if ( total_len >= C_BaseUnit::max_label_size ) {
-                                fprintf( stderr, "Combined label for an <instance> (\"%s%s\") exceeding %zu characters near line %d\n",
-                                         group_prefix, id_s, C_BaseUnit::max_label_size, n->line);
-                                throw TNMLIOResult::biglabel;
-                        }
-                        _longest_label = max(
-                                _longest_label,
-                                (unsigned short)snprintf(
-                                        cell_id, C_BaseUnit::max_label_size-1, "%s.%s",
-                                        group_prefix, id_s));  // here, a new instance is given a name
-                        xmlFree( id_s);
-
                         if ( !(nin = n->children) )
                                 return retval;
 
@@ -357,24 +338,27 @@ _process_population_instances(
                                 xmlChar *x_s = xmlGetProp( nin, BAD_CAST "x"),
                                         *y_s = xmlGetProp( nin, BAD_CAST "y"),
                                         *z_s = xmlGetProp( nin, BAD_CAST "z");
-                              // here we do actually insert neurons into the model
+                                // here we do actually insert neurons into the model
                                 if ( !(x_s && y_s && z_s) )
                                         vp( 1, stderr, "<location> element missing full set of coordinates near line %d\n", nin->line);
-                                        // not an error
+                                // not an error
                                 x = strtod( (char*)x_s, nullptr), y = strtod( (char*)y_s, nullptr), z = strtod( (char*)z_s, nullptr);
-                                xmlFree( x_s), xmlFree( y_s), xmlFree( z_s);
+                                auto maybe_free = [](decltype(x_s) X) { if ( X ) free((void*)X); };
+                                maybe_free( x_s), maybe_free( y_s), maybe_free( z_s);
 
                                 C_BaseNeuron *neu = add_neuron_species(
-                                        (char*)type_s, cell_id,
-                                        TIncludeOption::is_notlast);
+                                        (char*)type_s, (char*)population, (char*)id_s,
+                                        TIncludeOption::is_notlast,
+                                        x, y, z);
+
+                                xmlFree( id_s);
 
                                 if ( !neu || neu->_status & CN_UERROR ) {
                                         if ( neu )
                                                 delete neu;
-                                        fprintf( stderr, "Failed to add a neuron \"%s\" near line %u\n", cell_id, n->line);
+                                        fprintf( stderr, "Failed to add neuron of type %s, population:id \"%s:%s\" near line %u\n", type_s, population, id_s, n->line);
                                         return TNMLIOResult::structerror;
                                 } else {
-                                        neu->_serial_id = _global_unit_id_reservoir++;
                                         neu->pos = make_tuple( x, y, z);
                                         ++retval;
                                 }
@@ -394,8 +378,8 @@ _process_population_instances(
 int
 cnrun::CModel::
 _process_projection_connections(
-        xmlNode *n,
-        const xmlChar *synapse_name,
+        xmlNode *N,
+        const xmlChar *unused_synapse_name,
         const xmlChar *type_s,
         const xmlChar *src_grp_prefix,
         const xmlChar *tgt_grp_prefix)
@@ -405,9 +389,7 @@ _process_projection_connections(
 
         int     retval = 0;  // is also a counter of synapses
 
-        char    //synapse_id [C_BaseUnit::max_label_size],
-                src_s[C_BaseUnit::max_label_size],
-                tgt_s[C_BaseUnit::max_label_size];
+        string  label_src, label_tgt;
         double  weight;
 
         C_BaseSynapse *y;
@@ -416,31 +398,38 @@ _process_projection_connections(
                 *tgt_cell_id_s = nullptr,
                 *weight_s      = nullptr;
         try {
-                for ( ; n; n = n->next ) {
-                        if ( n->type != XML_ELEMENT_NODE || !xmlStrEqual( n->name, BAD_CAST "connection") )
+                for ( ; N; N = N->next ) {
+                        if ( N->type != XML_ELEMENT_NODE || !xmlStrEqual( N->name, BAD_CAST "connection") )
                                 continue;
 
-                        src_cell_id_s = xmlGetProp( n, BAD_CAST "pre_cell_id"),
-                        tgt_cell_id_s = xmlGetProp( n, BAD_CAST "post_cell_id"),
-                        weight_s      = xmlGetProp( n, BAD_CAST "weight");
+                        src_cell_id_s = xmlGetProp( N, BAD_CAST "pre_cell_id"),
+                        tgt_cell_id_s = xmlGetProp( N, BAD_CAST "post_cell_id"),
+                        weight_s      = xmlGetProp( N, BAD_CAST "weight");
                         if ( /*!synapse_id_s || */ !src_cell_id_s || !tgt_cell_id_s ) {
-                                fprintf( stderr, "A <connection> element without \"pre_cell_id\" and/or \"post_cell_id\" attribute near line %u\n", n->line);
+                                fprintf( stderr, "A <connection> element without \"pre_cell_id\" and/or \"post_cell_id\" attribute near line %u\n", N->line);
                                 throw TNMLIOResult::badattr;
                         }
 
-                        snprintf( src_s, C_BaseUnit::max_label_size-1, "%s.%s", src_grp_prefix, src_cell_id_s);
-                        snprintf( tgt_s, C_BaseUnit::max_label_size-1, "%s.%s", tgt_grp_prefix, tgt_cell_id_s);
+                        label_src = C_BaseNeuron::make_nml_name( (char*)src_grp_prefix, (char*)src_cell_id_s);
+                        label_tgt = C_BaseNeuron::make_nml_name( (char*)tgt_grp_prefix, (char*)tgt_cell_id_s);
+                        if ( label_src.size() > C_BaseUnit::max_label_size ||
+                             label_tgt.size() > C_BaseUnit::max_label_size ) {
+                                fprintf( stderr, "Source or target label size (%zu) exceeds %zu chars near line %u\n",
+                                         max(label_tgt.size(), label_src.size()), C_BaseUnit::max_label_size, N->line);
+                                return TNMLIOResult::biglabel;
+                        }
 
                         if ( !weight_s ) {
                                 vp( 3, stderr, "Assuming 0 for a synapse of \"%s.%s\" to \"%s%s\" without a \"weight\" attribute near line %u\n",
-                                    src_grp_prefix, src_cell_id_s, tgt_grp_prefix, tgt_cell_id_s, n->line);
+                                    src_grp_prefix, src_cell_id_s, tgt_grp_prefix, tgt_cell_id_s, N->line);
                                 weight = 0.;
                         }
                         /* xmlFree( synapse_id_s), */ xmlFree( src_cell_id_s), xmlFree( tgt_cell_id_s),
                                 xmlFree( weight_s);
 
                         y = add_synapse_species(
-                                (char*)type_s, src_s, tgt_s,
+                                (char*)type_s,
+                                label_src.c_str(), label_tgt.c_str(),
                                 weight,
                                 TSynapseCloningOption::yes,
                                 TIncludeOption::is_notlast);
@@ -449,7 +438,7 @@ _process_projection_connections(
                                 if ( y )
                                         delete y;
                                 fprintf( stderr, "Failed to add an \"%s\" synapse from \"%s\" to \"%s\" near line %u\n",
-                                         (char*)type_s, src_s, tgt_s, n->line);
+                                         (char*)type_s, label_src.c_str(), label_tgt.c_str(), N->line);
                                 return TNMLIOResult::structerror;
                         } else
                                 ++retval;
@@ -472,9 +461,111 @@ export_NetworkML( const string& fname)
 {
         int retval = 0;
 
-        LIBXML_TEST_VERSION;
+        xmlIndentTreeOutput = 1;
+
+        xmlDoc  *doc = xmlNewDoc( BAD_CAST "1.0");
+
+        xmlNode *root_node = xmlNewDocNode( doc, NULL, BAD_CAST "networkml", NULL);
+        xmlDocSetRootElement( doc, root_node);
+        // This is from m.nml, originally generated by neuroConstruct ca 2009:
+        // <networkml xmlns="http://morphml.org/networkml/schema"
+        //            xmlns:meta="http://morphml.org/metadata/schema"
+        //            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        //            xsi:schemaLocation="http://morphml.org/networkml/schema NetworkML_v1.7.xsd"
+        //            lengthUnits="micron">
+        // The definitions below are from https://github.com/NeuroML/NeuroML2/blob/master/examples/NML2_FullNeuroML.nml:
+        xmlNs *ns = xmlNewNs( root_node, BAD_CAST "http://www.neuroml.org/schema/neuroml2", NULL);
+        xmlNewNsProp( root_node, ns, BAD_CAST "xmlns:meta", BAD_CAST "http://morphml.org/metadata/schema");
+        xmlNewNsProp( root_node, ns, BAD_CAST "xmlns:xsi", BAD_CAST "http://www.w3.org/2001/XMLSchema-instance");
+        xmlNewNsProp( root_node, ns, BAD_CAST "xsi:schemaLocation", BAD_CAST "http://www.neuroml.org/schema/neuroml2 ../Schemas/NeuroML2/NeuroML_v2beta4.xsd");
+
+        {
+                time_t t = time(NULL);
+                string manifest = sasprintf(
+                        "Model %s\n"
+                        "Created with CNRun2 v. " VERSION " on %s",
+                        name.c_str(), asctime( localtime( &t)));
+                xmlNewTextChild( root_node, ns, BAD_CAST "meta:notes", BAD_CAST manifest.c_str());
+        }
 
-        fprintf( stderr, "export_NetworkML() not implemented yet\n");
+        xmlNode *populations_node = xmlNewNode( NULL, BAD_CAST "populations"),
+                *projections_node = xmlNewNode( NULL, BAD_CAST "projections");
+        xmlAddChild( root_node, populations_node);
+        xmlAddChild( root_node, projections_node);
+
+        map<string, xmlNode*> PP;
+        auto get_or_create_population = [&] (const char *population,
+                                             const char *cell_type) -> xmlNode* {
+                auto found = PP.find(string(population));
+                if ( found == PP.end() ) {
+                        xmlNode *N = xmlNewChild( populations_node, NULL, BAD_CAST "population", NULL);
+                        xmlNewProp( N, BAD_CAST "name", BAD_CAST population);
+                        xmlNewProp( N, BAD_CAST "cell_type", BAD_CAST cell_type);
+                        return PP[population] = xmlNewChild( N, NULL, BAD_CAST "instances", NULL);
+                }
+                return found->second;
+        };
+        auto get_or_create_projection = [&] (const char *projection,
+                                             const char *source,
+                                             const char *target,
+                                             const char *synapse_type,
+                                             const char *weight) -> xmlNode* {
+                auto found = PP.find(string(projection));
+                if ( found == PP.end() ) {  // is always true
+                        xmlNode *N = xmlNewChild( projections_node, NULL, BAD_CAST "projection", NULL);
+                        xmlNewProp( N, BAD_CAST "name", BAD_CAST projection);
+                        xmlNewProp( N, BAD_CAST "source", BAD_CAST source);
+                        xmlNewProp( N, BAD_CAST "target", BAD_CAST target);
+                        xmlNode *Nsyn_props = xmlNewChild( N, NULL, BAD_CAST "synapse_props", NULL);
+                        xmlNewProp( Nsyn_props, BAD_CAST "synapse_type", BAD_CAST synapse_type);
+                        xmlNewProp( Nsyn_props, BAD_CAST "weight", BAD_CAST weight);
+                        return PP[projection] = xmlNewChild( N, NULL, BAD_CAST "connections", NULL);
+                }
+                return found->second;
+        };
+        for ( const auto& U : units )
+                if ( U->is_neuron() ) {
+                        const auto& N = reinterpret_cast<C_BaseNeuron*>(U);
+                        xmlNode *instances_node =
+                                get_or_create_population(
+                                        U->population(),
+                                        U->species());
+                        xmlNode *instance_node = xmlNewChild( instances_node, NULL, BAD_CAST "instance", NULL);
+                        xmlNewProp(instance_node, BAD_CAST "id", BAD_CAST U->id());
+                        xmlNode *location_node = xmlNewChild( instance_node, NULL, BAD_CAST "location", NULL);
+                        xmlNewProp(location_node, BAD_CAST "x", BAD_CAST sasprintf("%g", N->pos.x).c_str());
+                        xmlNewProp(location_node, BAD_CAST "y", BAD_CAST sasprintf("%g", N->pos.y).c_str());
+                        xmlNewProp(location_node, BAD_CAST "z", BAD_CAST sasprintf("%g", N->pos.z).c_str());
+                } else if ( U->is_synapse() ) {
+                        const auto& Y = reinterpret_cast<C_BaseSynapse*>(U);
+                        string  src_pop, src_id,
+                                tgt_pop, tgt_id;
+                        C_BaseNeuron::extract_nml_parts(
+                                Y->source()->label(),
+                                &src_pop, &src_id);
+                        for ( const auto T : Y->targets() ) {
+                                C_BaseNeuron::extract_nml_parts(
+                                        T->label(),
+                                        &tgt_pop, &tgt_id);
+                                xmlNode *projection_node =
+                                        get_or_create_projection(
+                                                (src_pop + "-" + tgt_pop).c_str(),
+                                                src_pop.c_str(), tgt_pop.c_str(),
+                                                Y->type_s(), "1.0");
+                                xmlNode *connection_node = xmlNewChild( projection_node, NULL, BAD_CAST "connection", NULL);
+                                xmlNewProp( connection_node, BAD_CAST "pre_cell_id", BAD_CAST src_id.c_str());
+                                xmlNewProp( connection_node, BAD_CAST "post_cell_id", BAD_CAST tgt_id.c_str());
+                        }
+                }
+        {
+                FILE* fout = fopen( fname.c_str(), "w");
+                if ( !fout )
+                        return TNMLIOResult::nofile;
+                xmlDocFormatDump( fout, doc, 1);
+                fclose( fout);
+        }
+        xmlFreeDoc( doc);
+        xmlCleanupParser();
 
         return retval;
 }
diff --git a/upstream/src/libcnrun/model/struct.cc b/upstream/src/libcnrun/model/struct.cc
index 355e939..21bdace 100644
--- a/upstream/src/libcnrun/model/struct.cc
+++ b/upstream/src/libcnrun/model/struct.cc
@@ -35,7 +35,6 @@ CModel (const string& inname,
         const SModelOptions& inoptions)
       : name (inname),
         options (inoptions),
-        _global_unit_id_reservoir (0l),
         V (1),
         W (1),
         _var_cnt (1),                        // reserve [0] for model_time
@@ -189,7 +188,6 @@ _include_base_unit( C_BaseUnit* u)
         }
 
         u->M = this;
-        u->_serial_id = _global_unit_id_reservoir++;
 }
 
 
@@ -443,71 +441,71 @@ unregister_unit_with_sources( C_BaseUnit *u)
 
 cnrun::C_BaseNeuron*
 cnrun::CModel::
-add_neuron_species( const string& type_s, const string& label,
+add_neuron_species( const char* type_s, const char* population, const char* id,
                     const TIncludeOption include_option,
                     const double x, const double y, const double z)
 {
         TUnitType t = unit_species_by_string( type_s);
         if ( unlikely (t == NT_VOID || !unit_species_is_neuron(type_s)) ) {
-                fprintf( stderr, "Unrecognised neuron species: \"%s\"\n", type_s.c_str());
+                fprintf( stderr, "Unrecognised neuron species: \"%s\"\n", type_s);
                 return nullptr;
         } else
-                return add_neuron_species( t, label, include_option, x, y, z);
+                return add_neuron_species( t, population, id, include_option, x, y, z);
 }
 
 cnrun::C_BaseNeuron*
 cnrun::CModel::
-add_neuron_species( TUnitType type, const string& label,
+add_neuron_species( TUnitType type, const char* population, const char* id,
                     const TIncludeOption include_option,
                     double x, double y, double z)
 {
         C_BaseNeuron *n;
         switch ( type ) {
         case NT_HH_D:
-                n = new CNeuronHH_d( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new CNeuronHH_d( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
         case NT_HH_R:
-                n = new CNeuronHH_r( label, x, y, z, this, CN_UOWNED);
+                n = new CNeuronHH_r( population, id, x, y, z, this, CN_UOWNED);
             break;
 
         case NT_HH2_D:
-                n = new CNeuronHH2_d( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new CNeuronHH2_d( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
         // case NT_HH2_R:
-        //         n = new CNeuronHH2_r( label, x, y, z, this, CN_UOWNED, include_option);
+        //         n = new CNeuronHH2_r( population, id, x, y, z, this, CN_UOWNED, include_option);
         //     break;
 //#ifdef CN_WANT_MORE_NEURONS
         case NT_EC_D:
-                n = new CNeuronEC_d( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new CNeuronEC_d( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
         case NT_ECA_D:
-                n = new CNeuronECA_d( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new CNeuronECA_d( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
 /*
         case NT_LV:
-                n = new COscillatorLV( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new COscillatorLV( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
  */
         case NT_COLPITTS:
-                n = new COscillatorColpitts( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new COscillatorColpitts( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
         case NT_VDPOL:
-                n = new COscillatorVdPol( label, x, y, z, this, CN_UOWNED, include_option);
+                n = new COscillatorVdPol( population, id, x, y, z, this, CN_UOWNED, include_option);
             break;
 //#endif
         case NT_DOTPOISSON:
-                n = new COscillatorDotPoisson( label, x, y, z, this, CN_UOWNED);
+                n = new COscillatorDotPoisson( population, id, x, y, z, this, CN_UOWNED);
             break;
         case NT_POISSON:
-                n = new COscillatorPoisson( label, x, y, z, this, CN_UOWNED);
+                n = new COscillatorPoisson( population, id, x, y, z, this, CN_UOWNED);
             break;
 
         case NT_DOTPULSE:
-                n = new CNeuronDotPulse( label, x, y, z, this, CN_UOWNED);
+                n = new CNeuronDotPulse( population, id, x, y, z, this, CN_UOWNED);
             break;
 
         case NT_MAP:
-                n = new CNeuronMap( label, x, y, z, this, CN_UOWNED);
+                n = new CNeuronMap( population, id, x, y, z, this, CN_UOWNED);
             break;
 
         default:
@@ -529,24 +527,25 @@ add_neuron_species( TUnitType type, const string& label,
 
 cnrun::C_BaseSynapse*
 cnrun::CModel::
-add_synapse_species( const string& type_s,
-                     const string& src_l, const string& tgt_l,
+add_synapse_species( const char *type_s,
+                     const char *src_s, const char *tgt_s,
                      const double g,
                      const TSynapseCloningOption cloning_option,
                      const TIncludeOption include_option)
 {
-        TUnitType ytype = unit_species_by_string( type_s);
+        TUnitType
+                ytype = unit_species_by_string( type_s);
         bool    given_species = true;
         if ( ytype == NT_VOID && (given_species = false, ytype = unit_family_by_string( type_s)) == NT_VOID ) {
-                vp( 0, stderr, "Unrecognised synapse species or family: \"%s\"\n", type_s.c_str());
+                vp( 0, stderr, "Unrecognised synapse species or family: \"%s\"\n", type_s);
                 return nullptr;
         }
 
         C_BaseNeuron
-                *src = neuron_by_label( src_l),
-                *tgt = neuron_by_label( tgt_l);
+                *src = neuron_by_label( src_s),
+                *tgt = neuron_by_label( tgt_s);
         if ( !src || !tgt ) {
-                vp( 0, stderr, "Phony source (\"%s\") or target (\"%s\")\n", src_l.c_str(), tgt_l.c_str());
+                vp( 0, stderr, "Non-existent source (\"%s\") or target (\"%s\")\n", src_s, tgt_s);
                 return nullptr;
         }
 
@@ -618,7 +617,7 @@ add_synapse_species( const string& type_s,
                 }
             break;
         default:
-                vp( 0, stderr, "Bad synapse type: %s\n", type_s.c_str());
+                vp( 0, stderr, "Bad synapse type: %s\n", type_s);
                 return nullptr;
         }
 
@@ -635,8 +634,8 @@ add_synapse_species( TUnitType ytype,
                      double g,
                      TSynapseCloningOption cloning_option, TIncludeOption include_option)
 {
-        vp( 5, "add_synapse_species( \"%s\", \"%s\", \"%s\", %g, %d, %d)\n",
-            __CNUDT[ytype].species, src->_label, tgt->_label, g, (int)cloning_option, (int)include_option);
+        vp( 5, "add_synapse_species( type: \"%s\", src: \"%s\", tgt: \"%s\", g: %g)\n",
+            __CNUDT[ytype].species, src->_label, tgt->_label, g);
 
         C_BaseSynapse *y = nullptr;
 
diff --git a/upstream/src/libcnrun/units/base-neuron.hh b/upstream/src/libcnrun/units/base-neuron.hh
index ad3f35e..332efbb 100644
--- a/upstream/src/libcnrun/units/base-neuron.hh
+++ b/upstream/src/libcnrun/units/base-neuron.hh
@@ -46,11 +46,11 @@ class C_BaseNeuron
         friend class C_BaseSynapse;
 
     protected:
-        C_BaseNeuron (TUnitType intype, const string& inlabel,
-                      double inx, double iny, double inz,
-                      CModel* inM, int s_mask = 0)
-              : C_BaseUnit (intype, inlabel, inM, s_mask),
-                pos (inx, iny, inz),
+        C_BaseNeuron (TUnitType type_, const char* pop_, const char* id_,
+                      double x_, double y_, double z_,
+                      CModel* M_, int s_mask = 0)
+              : C_BaseUnit (type_, pop_, id_, M_, s_mask),
+                pos (x_, y_, z_),
                 _spikelogger_agent (nullptr)
                 {}
 
diff --git a/upstream/src/libcnrun/units/base-synapse.hh b/upstream/src/libcnrun/units/base-synapse.hh
index ebad567..d52536a 100644
--- a/upstream/src/libcnrun/units/base-synapse.hh
+++ b/upstream/src/libcnrun/units/base-synapse.hh
@@ -52,7 +52,8 @@ class C_BaseSynapse
                         return cnrun::alg::member(
                                 const_cast<C_BaseNeuron*>(&tgt), _targets);
                 }
-        C_BaseNeuron* source()  {  return _source;  }
+        const C_BaseNeuron* source() const    {  return _source;  }
+        const list<C_BaseNeuron*>& targets()  {  return _targets; }
 
         double g_on_target( C_BaseNeuron&) const;
         void set_g_on_target( C_BaseNeuron&, double);
diff --git a/upstream/src/libcnrun/units/base-unit.cc b/upstream/src/libcnrun/units/base-unit.cc
index 5d72777..521a0ee 100644
--- a/upstream/src/libcnrun/units/base-unit.cc
+++ b/upstream/src/libcnrun/units/base-unit.cc
@@ -34,29 +34,90 @@ using cnrun::alg::member;
 unsigned short cnrun::global::precision = 4;
 int cnrun::global::verbosely = 1;
 
+string
 cnrun::C_BaseUnit::
-C_BaseUnit (TUnitType type_, const string& label_,
+make_nml_name( const char *population, const size_t id)
+{
+        return sasprintf("%s.%zu", population, id);
+}
+
+string
+cnrun::C_BaseUnit::
+make_nml_name( const char *population, const char *id)
+{
+        return sasprintf("%s.%s", population, id);
+}
+
+void
+cnrun::C_BaseUnit::
+extract_nml_parts( const char *label,
+                   string* population, string* id)
+{
+        auto parts = cnrun::stilton::str::tokens(label, ".");
+        if ( population )
+                *population = *parts.begin();
+        if ( id )
+                *id = *next(parts.begin());
+}
+
+cnrun::C_BaseUnit::
+C_BaseUnit (TUnitType type_, const char* population_, const char* id_,
             CModel* M_, int s_mask)
       : precision (global::precision),
         _type (type_), _status (0 |/* CN_UENABLED |*/ s_mask),
         M (M_),
         _binwrite_handle (-1), _listener_disk (nullptr), _listener_mem (nullptr)
 {
-        memset( _label, 0, max_label_size);
-        if ( label_.size() )
-                strncpy( _label, label_.c_str(), max_label_size);
-        else
-                snprintf( _label, max_label_size-1, "fafa%p", this);
-
-        if ( M_ && M_->unit_by_label( label_) ) {
-                fprintf( stderr, "Model %s already has a unit labelled \"%s\"\n", M_->name.c_str(), label_.c_str());
-                throw "Duplicate unit label";
-        }
-
+        set_population_id( population_, id_);
         reset_params();
         // don't have field idx to do reset_vars() safely
 }
 
+void
+cnrun::C_BaseUnit::C_BaseUnit::
+set_population_id( const char* pop_, const char* id_)
+{
+        if ( !pop_ && !id_ )
+                return;
+
+        const char* id  = id_  ? id_  : _id;
+        const char* pop = pop_ ? pop_ : _population;
+
+        if ( strlen(id) > max_id_size ) {
+                fprintf( stderr, "Unit id too long: \"%.*s\"", (int)max_id_size, id);
+                throw "Id too long";
+        }
+        if ( strlen(id) == 0 ) {
+                fprintf( stderr, "Empty unit id for unit in population \"%s\"", pop);
+                throw "Empty id";
+        }
+        if ( strlen(pop) == 0 ) {
+                fprintf( stderr, "Empty unit population (unit id \"%s\")", id);
+                throw "Empty population";
+        }
+        string prelabel = make_nml_name( pop, id);
+        if ( prelabel.size() > max_label_size ) {
+                fprintf( stderr, "Unit label too long: \"%.*s\"", (int)max_label_size, prelabel.c_str());
+                throw "Label too long";
+        }
+
+        if ( M && M->unit_by_label( prelabel) ) {
+                fprintf( stderr, "Model %s already has a unit labelled \"%s\"\n", M->name.c_str(), prelabel.c_str());
+                throw "Duplicate unit label";
+        }
+
+        memset( _label,      0, max_label_size);
+        memset( _population, 0, max_label_size);
+        memset( _id,         0, max_id_size);
+
+        strncpy( _label, prelabel.c_str(), max_label_size);
+        if ( pop == pop_ )
+                strncpy( _population, pop_, max_label_size);
+        if ( id == id_ )
+                strncpy( _id,         id,   max_id_size);
+
+        M -> update_longest_label( strlen(_label));
+}
 
 
 void
@@ -244,7 +305,7 @@ void
 cnrun::C_BaseUnit::
 dump( bool with_params, FILE *strm) const
 {
-        fprintf( strm, "[%lu] (%s) \"%s\"\n", _serial_id, species(), _label);
+        fprintf( strm, "(%s) \"%s\"\n", species(), _label);
 
         if ( with_params ) {
                 fprintf( strm, "    Pp: ");
@@ -542,17 +603,17 @@ n_spikes_since( double since) const
 // ----- CSynapse
 
 cnrun::C_BaseSynapse::
-C_BaseSynapse( TUnitType intype,
-               C_BaseNeuron *insource, C_BaseNeuron *intarget,
-               double ing, CModel *inM, int s_mask)
-      : C_BaseUnit (intype, "overwrite-me", inM, s_mask),
-        _source (insource),
+C_BaseSynapse (const TUnitType type_,
+               C_BaseNeuron *source_, C_BaseNeuron *target_,
+               const double g_, CModel *M_, int s_mask)
+      : C_BaseUnit (type_, "overwrite me", "and me", M_, s_mask),
+        _source (source_),
         t_last_release_started (-INFINITY)
 {
         if ( M )
                 M->vp( 5, "Creating a \"%s\" base synapse\n", species());
-        _targets.push_back( intarget);
-        intarget->_dendrites[this] = ing;
+        _targets.push_back( target_);
+        target_->_dendrites[this] = g_;
         _source->_axonal_harbour.push_back( this);
         snprintf( _label, max_label_size-1, "%s:1", _source->_label);
 }
@@ -658,6 +719,7 @@ cnrun::C_BaseSynapse::
         }
 }
 
+
 // Local Variables:
 // Mode: c++
 // indent-tabs-mode: nil
diff --git a/upstream/src/libcnrun/units/base-unit.hh b/upstream/src/libcnrun/units/base-unit.hh
index 569ce39..6b76013 100644
--- a/upstream/src/libcnrun/units/base-unit.hh
+++ b/upstream/src/libcnrun/units/base-unit.hh
@@ -63,16 +63,21 @@ extern int verbosely;
 // * listening, i.e., keeping a history of vars along a timeline;
 class C_BaseUnit {
 
-        DELETE_DEFAULT_METHODS (C_BaseUnit)
-
-        friend class CModel;
-        friend class SSpikeloggerService;
-
     public:
         static const constexpr size_t max_label_size = 40;
+        static const constexpr size_t max_id_size = 10;
+
+        static string make_nml_name( const char* population, const size_t id);
+        static string make_nml_name( const char* population, const char *id);
+        static void extract_nml_parts( const char* label, string* population_p, string* id_p);
 
     protected:
-        C_BaseUnit (TUnitType, const string& label,
+        DELETE_DEFAULT_METHODS (C_BaseUnit);
+
+        friend class CModel;
+        friend class SSpikeloggerService;
+
+        C_BaseUnit (TUnitType, const char* pop, const char *id,
                     CModel*, int s_mask);
     public:
         virtual ~C_BaseUnit();  // surely virtual
@@ -85,6 +90,7 @@ class C_BaseUnit {
 
       // classification
         int  traits()        const {  return __CNUDT[_type].traits;                  }
+        const char* type_s() const {  return __CNUDT[_type].species;                 }
         bool is_hostable()   const {  return __CNUDT[_type].traits & UT_HOSTED;      }
         bool is_ddtbound()   const {  return __CNUDT[_type].traits & UT_DDTSET;      }
         bool is_neuron()     const {  return _type >= NT_FIRST && _type <= NT_LAST;  }
@@ -92,20 +98,21 @@ class C_BaseUnit {
         bool is_oscillator() const {  return __CNUDT[_type].traits & UT_OSCILLATOR;  }
         bool is_conscious()  const {  return is_oscillator();                        }
 
-        unsigned long serial() const
-                {  return _serial_id;  }
-        const char *label() const  // for synapses, it is "%s:%d", src->label, targets.size()
+        const char* label() const  // for synapses, it is "%s:%d", src->label, targets.size()
                 {  return _label;  }
-        void set_label( const string& new_label)
-                {  strncpy( _label, new_label.c_str(), max_label_size-1); }
+        const char* population() const
+                {  return _population;  }
+        const char* id() const
+                {  return _id; }
+        void set_population_id( const char*, const char*);
 
-        const char *class_name() const
+        const char* class_name() const
                 {  return is_neuron() ? "Neuron" : "Synapse";  }
-        const char *species() const
+        const char* species() const
                 {  return __CNUDT[_type].species;              }
-        const char *family() const
+        const char* family() const
                 {  return __CNUDT[_type].family;               }
-        const char *type_description() const
+        const char* type_description() const
                 {  return __CNUDT[_type].description;          }
 
       // parent model
@@ -117,8 +124,8 @@ class C_BaseUnit {
         bool is_owned() const       { return _status & CN_UOWNED; }
 
       // parameter & variable names and symbols
-        const char *const param_name( size_t i)       const { return __CNUDT[_type].stock_param_names[i]; }
-        const char *const param_sym( size_t i)        const { return __CNUDT[_type].stock_param_syms[i];  }
+        const char* const param_name( size_t i)       const { return __CNUDT[_type].stock_param_names[i]; }
+        const char* const param_sym( size_t i)        const { return __CNUDT[_type].stock_param_syms[i];  }
         int param_idx_by_sym( const string&) const __attribute__ ((pure));
 
         const char *const var_name( size_t i)         const { return __CNUDT[_type].stock_var_names[i];   }
@@ -262,9 +269,9 @@ class C_BaseUnit {
                 _type;  // will look up p, pno and vno from __CNUDT using _type as index
         int     _status;
 
-        unsigned long
-                _serial_id;  // assigned incrementally as read by import_NetworkML
-        char    _label[max_label_size];
+        char    _label     [max_label_size],
+                _population[max_label_size],
+                _id        [max_id_size];
 
         CModel  *M;
 
diff --git a/upstream/src/libcnrun/units/hosted-neurons.cc b/upstream/src/libcnrun/units/hosted-neurons.cc
index d9a6125..9e9fadb 100644
--- a/upstream/src/libcnrun/units/hosted-neurons.cc
+++ b/upstream/src/libcnrun/units/hosted-neurons.cc
@@ -25,11 +25,11 @@
 
 
 cnrun::C_HostedNeuron::
-C_HostedNeuron (TUnitType intype, const string& inlabel,
-                double inx, double iny, double inz,
-                CModel* inM, int s_mask,
+C_HostedNeuron (const TUnitType type_, const char* pop_, const char* id_,
+                const double x_, const double y_, const double z_,
+                CModel* M_, int s_mask,
                 TIncludeOption include_option)
-      : C_BaseNeuron (intype, inlabel, inx, iny, inz, inM, s_mask)
+      : C_BaseNeuron (type_, pop_, id_, x_, y_, z_, M_, s_mask)
 {
         if ( M )
                 M->include_unit( this, include_option);
diff --git a/upstream/src/libcnrun/units/hosted-neurons.hh b/upstream/src/libcnrun/units/hosted-neurons.hh
index cf86043..7bd11b4 100644
--- a/upstream/src/libcnrun/units/hosted-neurons.hh
+++ b/upstream/src/libcnrun/units/hosted-neurons.hh
@@ -34,10 +34,10 @@ class C_HostedNeuron
         DELETE_DEFAULT_METHODS (C_HostedNeuron)
 
     protected:
-        C_HostedNeuron (TUnitType intype, const string& inlabel,
-                        double x, double y, double z,
+        C_HostedNeuron (TUnitType type_, const char* pop_, const char* id_,
+                        double, double, double,
                         CModel*, int s_mask,
-                        TIncludeOption include_option);
+                        TIncludeOption);
     public:
         void reset_vars();
         double &var_value( size_t);
@@ -54,11 +54,11 @@ class C_HostedConductanceBasedNeuron
         DELETE_DEFAULT_METHODS (C_HostedConductanceBasedNeuron)
 
     protected:
-        C_HostedConductanceBasedNeuron (TUnitType intype, const string& inlabel,
-                                        double inx, double iny, double inz,
-                                        CModel* inM, int s_mask,
+        C_HostedConductanceBasedNeuron (TUnitType type_, const char* pop_, const char* id_,
+                                        const double x_, const double y_, const double z_,
+                                        CModel* M_, int s_mask,
                                         TIncludeOption include_option)
-              : C_HostedNeuron (intype, inlabel, inx, iny, inz, inM, s_mask, include_option)
+              : C_HostedNeuron (type_, pop_, id_, x_, y_, z_, M_, s_mask, include_option)
                 {}
 
     public:
@@ -82,11 +82,11 @@ class C_HostedRateBasedNeuron
         DELETE_DEFAULT_METHODS (C_HostedRateBasedNeuron)
 
     protected:
-        C_HostedRateBasedNeuron (TUnitType intype, const string& inlabel,
-                                 double inx, double iny, double inz,
-                                 CModel* inM, int s_mask,
+        C_HostedRateBasedNeuron (TUnitType type_, const char *pop_, const char* id_,
+                                 const double x_, const double y_, const double z_,
+                                 CModel* M_, int s_mask,
                                  TIncludeOption include_option)
-              : C_HostedNeuron (intype, inlabel, inx, iny, inz, inM, s_mask, include_option)
+              : C_HostedNeuron (type_, pop_, id_, x_, y_, z_, M_, s_mask, include_option)
                 {}
 
     public:
@@ -110,20 +110,20 @@ class CNeuronHH_d
         DELETE_DEFAULT_METHODS (CNeuronHH_d)
 
     public:
-        CNeuronHH_d (const string& inlabel,
-                     double x, double y, double z,
-                     CModel *inM, int s_mask = 0,
+        CNeuronHH_d (const char *pop_, const char* id_,
+                     const double x_, const double y_, const double z_,
+                     CModel *M_, int s_mask = 0,
                      TIncludeOption include_option = TIncludeOption::is_last)
-              : C_HostedConductanceBasedNeuron (NT_HH_D, inlabel, x, y, z,
-                                                inM, s_mask, include_option)
+              : C_HostedConductanceBasedNeuron (NT_HH_D, pop_, id_, x_, y_, z_,
+                                                M_, s_mask, include_option)
                 {}
 
       // parameters (since gcc 4.4, accessible from within member functions defined outside class definition, gee!)
         enum {
                 gNa, ENa, gK,  EK, gl, El, Cmem,
-                alpha_m_a,        alpha_m_b,        alpha_m_c,        beta_m_a,        beta_m_b,        beta_m_c,
-                alpha_h_a,        alpha_h_b,        alpha_h_c,        beta_h_a,        beta_h_b,        beta_h_c,
-                alpha_n_a,        alpha_n_b,        alpha_n_c,        beta_n_a,        beta_n_b,        beta_n_c,
+                alpha_m_a, alpha_m_b, alpha_m_c, beta_m_a, beta_m_b, beta_m_c,
+                alpha_h_a, alpha_h_b, alpha_h_c, beta_h_a, beta_h_b, beta_h_c,
+                alpha_n_a, alpha_n_b, alpha_n_c, beta_n_a, beta_n_b, beta_n_c,
                 Idc,
         };
 
@@ -152,12 +152,12 @@ class CNeuronHH2_d
         DELETE_DEFAULT_METHODS (CNeuronHH2_d)
 
     public:
-        CNeuronHH2_d (const string& inlabel,
-                      double x, double y, double z,
-                      CModel *inM, int s_mask = 0,
+        CNeuronHH2_d (const char *pop_, const char* id_,
+                      const double x_, const double y_, const double z_,
+                      CModel *M_, int s_mask = 0,
                       TIncludeOption include_option = TIncludeOption::is_last)
-              : C_HostedConductanceBasedNeuron( NT_HH2_D, inlabel, x, y, z,
-                                                inM, s_mask, include_option)
+              : C_HostedConductanceBasedNeuron( NT_HH2_D, pop_, id_, x_, y_, z_,
+                                                M_, s_mask, include_option)
                 {}
 
         double   m( vector<double>& b) const { return b[idx+1]; }
@@ -182,11 +182,11 @@ class CNeuronEC_d
         DELETE_DEFAULT_METHODS (CNeuronEC_d)
 
     public:
-        CNeuronEC_d( const string& inlabel,
-                     double x, double y, double z,
+        CNeuronEC_d( const char *pop_, const char* id_,
+                     const double x, const double y, const double z,
                      CModel *inM, int s_mask = 0,
                      TIncludeOption include_option = TIncludeOption::is_last)
-              : C_HostedConductanceBasedNeuron (NT_EC_D, inlabel, x, y, z,
+              : C_HostedConductanceBasedNeuron (NT_EC_D, pop_, id_, x, y, z,
                                                 inM, s_mask, include_option)
                 {}
 
@@ -216,12 +216,12 @@ class CNeuronECA_d
         DELETE_DEFAULT_METHODS (CNeuronECA_d)
 
     public:
-        CNeuronECA_d( const string& inlabel,
-                      double x, double y, double z,
-                      CModel *inM, int s_mask = 0,
+        CNeuronECA_d( const char *pop_, const char* id_,
+                      const double x_, const double y_, const double z_,
+                      CModel *M_, int s_mask = 0,
                       TIncludeOption include_option = TIncludeOption::is_last)
-              : C_HostedConductanceBasedNeuron( NT_ECA_D, inlabel, x, y, z,
-                                                inM, s_mask, include_option)
+              : C_HostedConductanceBasedNeuron( NT_ECA_D, pop_, id_, x_, y_, z_,
+                                                M_, s_mask, include_option)
                 {}
 
         double      m( vector<double>& b) const { return b[idx+1]; }
@@ -258,12 +258,12 @@ class COscillatorColpitts
         DELETE_DEFAULT_METHODS (COscillatorColpitts)
 
     public:
-        COscillatorColpitts( const string& inlabel,
-                             double x, double y, double z,
-                             CModel *inM, int s_mask = 0,
+        COscillatorColpitts( const char *pop_, const char* id_,
+                             const double x_, const double y_, const double z_,
+                             CModel *M_, int s_mask = 0,
                              TIncludeOption include_option = TIncludeOption::is_last)
-              : C_HostedConductanceBasedNeuron (NT_COLPITTS, inlabel, x, y, z,
-                                                inM, s_mask, include_option)
+              : C_HostedConductanceBasedNeuron (NT_COLPITTS, pop_, id_, x_, y_, z_,
+                                                M_, s_mask, include_option)
                 {}
 
         double   x0( vector<double>& b) const { return b[idx+0]; }  // there's no E() for this one
@@ -321,12 +321,12 @@ class COscillatorVdPol
         DELETE_DEFAULT_METHODS (COscillatorVdPol)
 
      public:
-        COscillatorVdPol (const string& inlabel,
-                          double x, double y, double z,
-                          CModel *inM, int s_mask = 0,
+        COscillatorVdPol (const char *pop_, const char* id_,
+                          double x_, double y_, double z_,
+                          CModel *M_, int s_mask = 0,
                           TIncludeOption include_option = TIncludeOption::is_last)
-              : C_HostedConductanceBasedNeuron (NT_VDPOL, inlabel, x, y, z,
-                                                inM, s_mask, include_option)
+              : C_HostedConductanceBasedNeuron (NT_VDPOL, pop_, id_, x_, y_, z_,
+                                                M_, s_mask, include_option)
                 {}
 
         double   amp( vector<double>& b) const  { return b[idx+0]; }
diff --git a/upstream/src/libcnrun/units/standalone-neurons.cc b/upstream/src/libcnrun/units/standalone-neurons.cc
index 37f36fc..e92f14a 100644
--- a/upstream/src/libcnrun/units/standalone-neurons.cc
+++ b/upstream/src/libcnrun/units/standalone-neurons.cc
@@ -24,10 +24,10 @@
 
 
 cnrun::C_StandaloneNeuron::
-C_StandaloneNeuron (TUnitType type_, const string& label_,
-                    double x, double y, double z,
-                    CModel *M_, int s_mask)
-      : C_BaseNeuron( type_, label_, x, y, z, M_, s_mask),
+C_StandaloneNeuron (const TUnitType type_, const char* pop_, const char* id_,
+                    const double x_, const double y_, const double z_,
+                    CModel *M_, const int s_mask)
+      : C_BaseNeuron( type_, pop_, id_, x_, y_, z_, M_, s_mask),
         C_StandaloneAttributes( __CNUDT[type_].vno)
 {
         reset_vars();
@@ -334,14 +334,16 @@ const char* const cnrun::CN_VarSyms_NeuronMap[] = {
 
 
 cnrun::CNeuronMap::
-CNeuronMap (const string& inlabel, double x, double y, double z, CModel *inM, int s_mask)
-      : C_StandaloneConductanceBasedNeuron( NT_MAP, inlabel, x, y, z, inM, s_mask)
+CNeuronMap (const char* pop_, const char* id_,
+            const double x_, const double y_, const double z_,
+            CModel *M_, int s_mask)
+      : C_StandaloneConductanceBasedNeuron( NT_MAP, pop_, id_, x_, y_, z_, M_, s_mask)
 {
-        if ( inM ) {
-                if ( isfinite( inM->_discrete_dt) && inM->_discrete_dt != fixed_dt )
+        if ( M_ ) {
+                if ( isfinite( M_->_discrete_dt) && M_->_discrete_dt != fixed_dt )
                         throw "Inappropriate discrete dt";
 
-                inM -> _discrete_dt = fixed_dt;
+                M_ -> _discrete_dt = fixed_dt;
         }
 }
 
diff --git a/upstream/src/libcnrun/units/standalone-neurons.hh b/upstream/src/libcnrun/units/standalone-neurons.hh
index 7189059..a298f05 100644
--- a/upstream/src/libcnrun/units/standalone-neurons.hh
+++ b/upstream/src/libcnrun/units/standalone-neurons.hh
@@ -33,7 +33,7 @@ class C_StandaloneNeuron
         DELETE_DEFAULT_METHODS (C_StandaloneNeuron)
 
     protected:
-        C_StandaloneNeuron (TUnitType intype, const string& inlabel,
+        C_StandaloneNeuron (TUnitType, const char* pop, const char* id,
                             double x, double y, double z,
                             CModel*, int s_mask);
 
@@ -57,10 +57,10 @@ class C_StandaloneConductanceBasedNeuron
         DELETE_DEFAULT_METHODS (C_StandaloneConductanceBasedNeuron)
 
     protected:
-        C_StandaloneConductanceBasedNeuron (TUnitType intype, const string& inlabel,
-                                            double inx, double iny, double inz,
-                                            CModel *inM, int s_mask)
-              : C_StandaloneNeuron (intype, inlabel, inx, iny, inz, inM, s_mask)
+        C_StandaloneConductanceBasedNeuron (const TUnitType type_, const char* pop_, const char* id_,
+                                            const double x_, const double y_, const double z_,
+                                            CModel *M_, int s_mask)
+              : C_StandaloneNeuron (type_, pop_, id_, x_, y_, z_, M_, s_mask)
                 {}
 
     public:
@@ -77,10 +77,10 @@ class C_StandaloneRateBasedNeuron
         DELETE_DEFAULT_METHODS (C_StandaloneRateBasedNeuron)
 
     protected:
-        C_StandaloneRateBasedNeuron (TUnitType intype, const string& inlabel,
-                                     double inx, double iny, double inz,
-                                     CModel *inM, int s_mask)
-              : C_StandaloneNeuron (intype, inlabel, inx, iny, inz, inM, s_mask)
+        C_StandaloneRateBasedNeuron (const TUnitType type_, const char* pop_, const char* id_,
+                                     const double x_, const double y_, const double z_,
+                                     CModel *M_, int s_mask)
+              : C_StandaloneNeuron (type_, pop_, id_, x_, y_, z_, M_, s_mask)
                 {}
 
     public:
@@ -100,10 +100,10 @@ class CNeuronHH_r
         DELETE_DEFAULT_METHODS (CNeuronHH_r)
 
     public:
-        CNeuronHH_r( const string& inlabel,
-                     double x, double y, double z,
-                     CModel *inM, int s_mask = 0)
-              : C_StandaloneRateBasedNeuron( NT_HH_R, inlabel, x, y, z, inM, s_mask)
+        CNeuronHH_r( const char* pop_, const char* id_,
+                     const double x_, const double y_, const double z_,
+                     CModel *M_, int s_mask = 0)
+              : C_StandaloneRateBasedNeuron( NT_HH_R, pop_, id_, x_, y_, z_, M_, s_mask)
                 {}
 
         enum {
@@ -128,10 +128,10 @@ class COscillatorPoisson
         DELETE_DEFAULT_METHODS (COscillatorPoisson)
 
     public:
-        COscillatorPoisson( const string& inlabel,
-                            double x, double y, double z,
-                            CModel *inM, int s_mask = 0)
-              : C_StandaloneConductanceBasedNeuron (NT_POISSON, inlabel, x, y, z, inM, s_mask)
+        COscillatorPoisson( const char* pop_, const char* id_,
+                            const double x_, const double y_, const double z_,
+                            CModel *M_, int s_mask = 0)
+              : C_StandaloneConductanceBasedNeuron (NT_POISSON, pop_, id_, x_, y_, z_, M_, s_mask)
                 {
                       // need _spikelogger_agent's fields even when no spikelogging is done
                         _spikelogger_agent = new SSpikeloggerService(
@@ -163,10 +163,10 @@ class COscillatorDotPoisson
         DELETE_DEFAULT_METHODS (COscillatorDotPoisson)
 
     public:
-        COscillatorDotPoisson (const string& inlabel,
-                               double x, double y, double z,
-                               CModel *inM, int s_mask = 0)
-              : C_StandaloneConductanceBasedNeuron( NT_DOTPOISSON, inlabel, x, y, z, inM, s_mask)
+        COscillatorDotPoisson (const char* pop_, const char* id_,
+                               const double x_, const double y_, const double z_,
+                               CModel *M_, int s_mask = 0)
+              : C_StandaloneConductanceBasedNeuron( NT_DOTPOISSON, pop_, id_, x_, y_, z_, M_, s_mask)
                 {
                       // need _spikelogger_agent's fields even when no spikelogging is done
                         _spikelogger_agent = new SSpikeloggerService(
@@ -197,10 +197,10 @@ class CNeuronDotPulse
         DELETE_DEFAULT_METHODS (CNeuronDotPulse)
 
     public:
-        CNeuronDotPulse (const string& inlabel,
-                         double x, double y, double z,
-                         CModel *inM, int s_mask = 0)
-              : C_StandaloneConductanceBasedNeuron (NT_DOTPULSE, inlabel, x, y, z, inM, s_mask)
+        CNeuronDotPulse (const char* pop_, const char* id_,
+                         const double x_, const double y_, const double z_,
+                         CModel *M_, int s_mask = 0)
+              : C_StandaloneConductanceBasedNeuron (NT_DOTPULSE, pop_, id_, x_, y_, z_, M_, s_mask)
                 {}
 
         enum { _f_, _Vrst_, _Vfir_ };
@@ -229,7 +229,8 @@ class CNeuronMap
     public:
         static const constexpr double fixed_dt = 0.1;
 
-        CNeuronMap (const string& inlabel, double x, double y, double z,
+        CNeuronMap (const char* pop_, const char* id_,
+                    double x_, double y_, double z_,
                     CModel*, int s_mask = 0);
 
         enum {
diff --git a/upstream/src/lua-cnrun/commands.cc b/upstream/src/lua-cnrun/commands.cc
index 3ffee50..c91386e 100644
--- a/upstream/src/lua-cnrun/commands.cc
+++ b/upstream/src/lua-cnrun/commands.cc
@@ -442,8 +442,10 @@ int new_neuron( lua_State *L)
                 *type  = lua_tostring( L, 3),
                 *label = lua_tostring( L, 4);
 
+        string population, id;
+        C_BaseUnit::extract_nml_parts( label, &population, &id);
         if ( !M.add_neuron_species(
-                     type, label,
+                     type, population.c_str(), id.c_str(),
                      TIncludeOption::is_last) )
                 return make_error(
                         L, "%s(%s): error", __FUNCTION__, model_name);
diff --git a/upstream/src/tools/hh-latency-estimator.cc b/upstream/src/tools/hh-latency-estimator.cc
index ee63d50..4fad6ac 100644
--- a/upstream/src/tools/hh-latency-estimator.cc
+++ b/upstream/src/tools/hh-latency-estimator.cc
@@ -116,10 +116,10 @@ main( int argc, char *argv[])
         Model->options.verbosely = 0;
 
       // add our three units
-        CNeuronHH_d  *hh    = new CNeuronHH_d( "HH", 0.2, 0.1, 0.3, Model, CN_UOWNED);
+        CNeuronHH_d  *hh    = new CNeuronHH_d( "HH", "0", 0.2, 0.1, 0.3, Model, CN_UOWNED);
         C_BaseNeuron *pulse = (Options.src_type == S_PULSE)
-                ? static_cast<C_BaseNeuron*>(new CNeuronDotPulse( "Pulse", 0.1, 0.2, 0.3, Model, CN_UOWNED))
-                : static_cast<C_BaseNeuron*>(new COscillatorDotPoisson( "Pulse", 0.1, 0.2, 0.3, Model, CN_UOWNED));
+                ? static_cast<C_BaseNeuron*>(new CNeuronDotPulse( "Pulse", "0", 0.1, 0.2, 0.3, Model, CN_UOWNED))
+                : static_cast<C_BaseNeuron*>(new COscillatorDotPoisson( "Pulse", "1", 0.1, 0.2, 0.3, Model, CN_UOWNED));
         CSynapseMxAB_dd *synapse = new CSynapseMxAB_dd( pulse, hh, Options.syn_g, Model, CN_UOWNED);
 
       // enable_spikelogging_service
@@ -166,13 +166,11 @@ main( int argc, char *argv[])
                 for ( i = 0; i < n_steps; i++ ) {
 
                         if ( Options.enable_listening ) {
-                                char label[C_BaseUnit::max_label_size];
-                                snprintf( label, C_BaseUnit::max_label_size, "pulse-%06g", frequencies[i]);
-                                pulse->set_label( label);
-                                snprintf( label, C_BaseUnit::max_label_size, "hh-%06g", frequencies[i]);
-                                hh->set_label( label);
-                                snprintf( label, C_BaseUnit::max_label_size, "synapse-%06g", frequencies[i]);
-                                synapse->set_label( label);
+                                auto set_pop = [&frequencies,&i](C_BaseUnit* U, const char* u_s)
+                                        { U->set_population_id( sasprintf( "%s-%06g", u_s, frequencies[i]).c_str(), nullptr); };
+                                set_pop(hh, "hh");
+                                set_pop(pulse, "pulse");
+                                set_pop(synapse, "synapse");
                         }
                         Model->reset();  // will reset model_time, preserve params, and is a generally good thing
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/cnrun.git



More information about the debian-med-commit mailing list