From 6cf2687ea392ba3d3518ee8e7d9abe99cd86705e Mon Sep 17 00:00:00 2001 From: Thorsten Hater <24411438+thorstenhater@users.noreply.github.com> Date: Thu, 23 Jun 2022 09:31:16 +0200 Subject: [PATCH] Predefine SWC Regions. (#1911) --- arbor/include/arbor/morph/label_dict.hpp | 7 ++- arbor/morph/label_dict.cpp | 14 +++++- doc/fileformat/swc.rst | 8 ++++ doc/tutorial/single_cell_detailed.rst | 44 +++++++++++++++---- python/cells.cpp | 10 ++++- python/example/gap_junctions.py | 4 +- python/example/single_cell_allen.py | 11 +---- python/example/single_cell_detailed.py | 12 ++--- python/example/single_cell_detailed_recipe.py | 12 ++--- .../single_cell_extracellular_potentials.py | 3 +- python/example/single_cell_swc.py | 16 +++---- python/proxy.hpp | 10 ++++- 12 files changed, 98 insertions(+), 53 deletions(-) diff --git a/arbor/include/arbor/morph/label_dict.hpp b/arbor/include/arbor/morph/label_dict.hpp index 6b590b7c..b9d415e8 100644 --- a/arbor/include/arbor/morph/label_dict.hpp +++ b/arbor/include/arbor/morph/label_dict.hpp @@ -17,10 +17,13 @@ class ARB_ARBOR_API label_dict { reg_map regions_; public: + // construct a label dict with SWC tags predefined + label_dict& add_swc_tags(); + void import(const label_dict& other, const std::string& prefix = ""); - void set(const std::string& name, locset ls); - void set(const std::string& name, region reg); + label_dict& set(const std::string& name, locset ls); + label_dict& set(const std::string& name, region reg); std::optional<arb::region> region(const std::string& name) const; std::optional<arb::locset> locset(const std::string& name) const; diff --git a/arbor/morph/label_dict.cpp b/arbor/morph/label_dict.cpp index 9c514604..bc6bca89 100644 --- a/arbor/morph/label_dict.cpp +++ b/arbor/morph/label_dict.cpp @@ -9,22 +9,32 @@ namespace arb { +label_dict& label_dict::add_swc_tags() { + set("soma", reg::tagged(1)); + set("axon", reg::tagged(2)); + set("dend", reg::tagged(3)); + set("apic", reg::tagged(4)); + return *this; +} + size_t label_dict::size() const { return locsets_.size() + regions_.size(); } -void label_dict::set(const std::string& name, arb::locset ls) { +label_dict& label_dict::set(const std::string& name, arb::locset ls) { if (regions_.count(name)) { throw label_type_mismatch(name); } locsets_[name] = std::move(ls); + return *this; } -void label_dict::set(const std::string& name, arb::region reg) { +label_dict& label_dict::set(const std::string& name, arb::region reg) { if (locsets_.count(name)) { throw label_type_mismatch(name); } regions_[name] = std::move(reg); + return *this; } void label_dict::import(const label_dict& other, const std::string& prefix) { diff --git a/doc/fileformat/swc.rst b/doc/fileformat/swc.rst index cfc9c34b..cf799324 100644 --- a/doc/fileformat/swc.rst +++ b/doc/fileformat/swc.rst @@ -36,6 +36,14 @@ its parent and inherits the tag of the sample; and if more than 1 sample have th is interpreted as a fork point in the morphology, and acts as the proximal point to a new branch for each of its "child" samples. There a couple of exceptions to these rules which are listed below. +.. Note:: + + The SWC file format allows association of ``tags`` with parts of the + morphology and reserves tag values 1-4 for soma, axon, basal dendrite, and + apical dendrite. In Arbor, these tags can be added to a + :class:`arbor.label_dict` using the :meth:`~arbor.label_dict.add_swc_tags` method. + + .. _formatswc-arbor: Arbor interpretation diff --git a/doc/tutorial/single_cell_detailed.rst b/doc/tutorial/single_cell_detailed.rst index 85e3f661..806bcf56 100644 --- a/doc/tutorial/single_cell_detailed.rst +++ b/doc/tutorial/single_cell_detailed.rst @@ -121,16 +121,44 @@ can be stored in a :class:`arbor.label_dict`. More information on region and location expressions is available :ref:`here <labels>`. -First, we can define some **regions**, These are continuous parts of the morphology, -They can correspond to full segments or parts of segments. Our morphology already has some -pre-established regions determined by the ``tag`` parameter of the segments. They are -defined as follows: +The SWC file format allows association of ``tags`` with parts of the morphology +and reserves tag values 1-4 for commonly used sections (see `here +<http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html>`__ +for the SWC file format). In Arbor, these tags can be added to a :class:`arbor.label_dict` using +the :meth:`~arbor.label_dict.add_swc_tags` method, which will define + +.. list-table:: Default SWC Tags + :widths: 25 25 50 + :header-rows: 1 + + * - Tag + - Label + - Section + * - 1 + - ``"soma"`` + - Soma + * - 2 + - ``"axon"`` + - Axon + * - 3 + - ``"dend"`` + - Basal dendrite + * - 4 + - ``"apic"`` + - Apical dendrite + +You can alternatively define these regions by hand, using -.. literalinclude:: ../../python/example/single_cell_detailed.py - :language: python - :lines: 22-32 +.. code-block:: python -This will generate the following regions when applied to the previously defined morphology: + labels = arbor.label_dict({ + "soma": "(tag 1)", + "axon": "(tag 2)", + "dend": "(tag 3)", + "apic": "(tag 4)", + }) + +Both ways will generate the following regions when applied to the previously defined morphology: .. figure:: ../gen-images/tutorial_tag.svg :width: 800 diff --git a/python/cells.cpp b/python/cells.cpp index bd4f1ba9..73384031 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -231,7 +231,8 @@ void register_cells(pybind11::module& m) { "A dictionary of labelled region and locset definitions, with a\n" "unique label assigned to each definition."); label_dict - .def(pybind11::init<>(), "Create an empty label dictionary.") + .def(pybind11::init<>(), + "Create an empty label dictionary.") .def(pybind11::init<const std::unordered_map<std::string, std::string>&>(), "Initialize a label dictionary from a dictionary with string labels as keys," " and corresponding definitions as strings.") @@ -248,6 +249,13 @@ void register_cells(pybind11::module& m) { return ld; }), "Initialize a label dictionary from an iterable of key, definition pairs") + .def("add_swc_tags", + [](label_dict_proxy& l) { return l.add_swc_tags(); }, + "Add standard SWC tagged regions.\n" + " - soma: (tag 1)\n" + " - axon: (tag 2)\n" + " - dend: (tag 3)\n" + " - apic: (tag 4)") .def("__setitem__", [](label_dict_proxy& l, const char* name, const char* desc) { l.set(name, desc);}) diff --git a/python/example/gap_junctions.py b/python/example/gap_junctions.py index 2076f02a..2541abd0 100644 --- a/python/example/gap_junctions.py +++ b/python/example/gap_junctions.py @@ -32,9 +32,7 @@ def make_cable_cell(gid): tree.append(s, arbor.mpoint(0, 0, 0, 2), arbor.mpoint(40, 0, 0, 2), tag=2) # Label dictionary for cell components - labels = arbor.label_dict() - labels["soma"] = "(tag 1)" - labels["dend"] = "(tag 2)" + labels = arbor.label_dict().add_swc_tags() # Mark location for synapse site at midpoint of dendrite (branch 0 = soma + dendrite) labels["synapse_site"] = "(location 0 0.6)" diff --git a/python/example/single_cell_allen.py b/python/example/single_cell_allen.py index 11276199..273d5dc2 100644 --- a/python/example/single_cell_allen.py +++ b/python/example/single_cell_allen.py @@ -75,15 +75,8 @@ def make_cell(swc, fit): morphology = arbor.load_swc_neuron(swc) # (2) Label the region tags found in the swc with the names used in the parameter fit file. # In addition, label the midpoint of the somarbor. - labels = arbor.label_dict( - { - "soma": "(tag 1)", - "axon": "(tag 2)", - "dend": "(tag 3)", - "apic": "(tag 4)", - "midpoint": "(location 0 0.5)", - } - ) + labels = arbor.label_dict().add_swc_tags() + labels["midpoint"] = "(location 0 0.5)" # (3) A function that parses the Allen parameter fit file into components for an arbor.decor dflt, regions, ions, mechanisms, offset = load_allen_fit(fit) diff --git a/python/example/single_cell_detailed.py b/python/example/single_cell_detailed.py index 30f081b6..afff9173 100755 --- a/python/example/single_cell_detailed.py +++ b/python/example/single_cell_detailed.py @@ -21,21 +21,17 @@ morph = arbor.load_swc_arbor(filename) # (2) Create and populate the label dictionary. -labels = arbor.label_dict() +# Pre-defined SWC regions +labels = arbor.label_dict().add_swc_tags() # Regions: -# Add labels for tag 1, 2, 3, 4 -labels["soma"] = "(tag 1)" -labels["axon"] = "(tag 2)" -labels["dend"] = "(tag 3)" -labels["last"] = "(tag 4)" # Add a label for a region that includes the whole morphology labels["all"] = "(all)" # Add a label for the parts of the morphology with radius greater than 1.5 μm. labels["gt_1.5"] = '(radius-ge (region "all") 1.5)' -# Join regions "last" and "gt_1.5" -labels["custom"] = '(join (region "last") (region "gt_1.5"))' +# Join regions "apic" and "gt_1.5" +labels["custom"] = '(join (region "apic") (region "gt_1.5"))' # Locsets: diff --git a/python/example/single_cell_detailed_recipe.py b/python/example/single_cell_detailed_recipe.py index ef1a4497..5bcd19f8 100644 --- a/python/example/single_cell_detailed_recipe.py +++ b/python/example/single_cell_detailed_recipe.py @@ -21,21 +21,17 @@ morph = arbor.load_swc_arbor(filename) # (2) Create and populate the label dictionary. -labels = arbor.label_dict() +# Label dict, with Pre-defined labels soma, axon, dend, and apic +labels = arbor.label_dict().add_swc_tags() # Regions: -# Add labels for tag 1, 2, 3, 4 -labels["soma"] = "(tag 1)" -labels["axon"] = "(tag 2)" -labels["dend"] = "(tag 3)" -labels["last"] = "(tag 4)" # Add a label for a region that includes the whole morphology labels["all"] = "(all)" # Add a label for the parts of the morphology with radius greater than 1.5 μm. labels["gt_1.5"] = '(radius-ge (region "all") 1.5)' -# Join regions "last" and "gt_1.5" -labels["custom"] = '(join (region "last") (region "gt_1.5"))' +# Join regions "apic" and "gt_1.5" +labels["custom"] = '(join (region "apic") (region "gt_1.5"))' # Locsets: diff --git a/python/example/single_cell_extracellular_potentials.py b/python/example/single_cell_extracellular_potentials.py index 413ebb90..9f99bcd7 100644 --- a/python/example/single_cell_extracellular_potentials.py +++ b/python/example/single_cell_extracellular_potentials.py @@ -74,8 +74,7 @@ def make_cable_cell(morphology, clamp_location): cvs_per_branch = 3 # Label dictionary - defs = {} - labels = arbor.label_dict(defs) + labels = arbor.label_dict() # decor decor = arbor.decor() diff --git a/python/example/single_cell_swc.py b/python/example/single_cell_swc.py index 06bdbf58..2a229399 100755 --- a/python/example/single_cell_swc.py +++ b/python/example/single_cell_swc.py @@ -26,15 +26,13 @@ filename = sys.argv[1] morpho = arbor.load_swc_arbor(filename) # Define the regions and locsets in the model. -defs = { - "soma": "(tag 1)", # soma has tag 1 in swc files. - "axon": "(tag 2)", # axon has tag 2 in swc files. - "dend": "(tag 3)", # dendrites have tag 3 in swc files. - "root": "(root)", # the start of the soma in this morphology is at the root of the cell. - "stim_site": "(location 0 0.5)", # site for the stimulus, in the middle of branch 0. - "axon_end": '(restrict (terminal) (region "axon"))', -} # end of the axon. -labels = arbor.label_dict(defs) +labels = arbor.label_dict( + { + "root": "(root)", # the start of the soma in this morphology is at the root of the cell. + "stim_site": "(location 0 0.5)", # site for the stimulus, in the middle of branch 0. + "axon_end": '(restrict (terminal) (region "axon"))', # end of the axon. NB. 'axon' will be added below + } +).add_swc_tags() # Finally, add the SWC default labels. decor = arbor.decor() diff --git a/python/proxy.hpp b/python/proxy.hpp index 65c44757..4d79f7c8 100644 --- a/python/proxy.hpp +++ b/python/proxy.hpp @@ -24,6 +24,14 @@ struct label_dict_proxy { } } + label_dict_proxy& add_swc_tags() { + set("soma", "(tag 1)"); + set("axon", "(tag 2)"); + set("dend", "(tag 3)"); + set("apic", "(tag 4)"); + return *this; + } + label_dict_proxy(const arb::label_dict& label_dict): dict(label_dict) { update_cache(); } @@ -55,7 +63,7 @@ struct label_dict_proxy { // (and vice versa.) // * the description is not well formed, e.g. it contains a syntax error. // * the description is well-formed, but describes neither a region or locset. - try{ + try { // Evaluate the s-expression to build a region/locset. auto result = arborio::parse_label_expression(desc); if (!result) { // an error parsing / evaluating description. -- GitLab