diff --git a/arbor/include/arbor/morph/label_dict.hpp b/arbor/include/arbor/morph/label_dict.hpp index 6b590b7c44d3594c42cb3985251db245d6a35d54..b9d415e868ab1e0895b7f6b43e594f8203d0a731 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 9c514604a16c11159b963828d8303138ef3b67f1..bc6bca89802df552f0d465cdc280fbdf70936515 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 cfc9c34bdfb654030d3ba0dcdfe126195f78d2ff..cf799324da8d54626f48462f46c2ba417625faa2 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 85e3f66130f858f30bab6289221c5d758975a40e..806bcf56d8f2b6d9fa4bed4640c991eb6d3f148b 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 bd4f1ba91653f0cfc63c9949a4802322f2d0df1d..73384031174dadc3cc455dbb22e7432699ec931d 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 2076f02a34850d19eaf752eedf2b9f79f379b1f7..2541abd0098d811c733b6ed4afbf8c3ec302282a 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 1127619907aec9606709caa3372ee3fc18941da0..273d5dc2f1cf09acf69e4464fd938a534d171bb3 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 30f081b6dc5e25f5d9aeae992027df1a79743c1a..afff91736d4ec9c715d6c67bd8f1ca43d7526632 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 ef1a4497e3a62e19c2506f8e1ee7d2dfa70d9f4e..5bcd19f8d6693e31f17d8d271fe4498bd8ec575d 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 413ebb90195caf6a20a2895c8203e1573e6d521c..9f99bcd7ce0fba18d9679094bb34e8af4ad381a6 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 06bdbf581944e26dd1f8f910a51ce138bd4f7ffe..2a229399396ec79ef948ed8b33ddda8938dcb87b 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 65c44757b3a8cac13357fc0f9459d8386ea1631c..4d79f7c8405ce4a0791913e7b1657c1146ee34de 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.