diff --git a/arbor/cable_cell_param.cpp b/arbor/cable_cell_param.cpp index 2f48b0a76ab44e41d451560de130c0b6b22d5cec..e272781b781bfc17d212a21f073b4452117450cb 100644 --- a/arbor/cable_cell_param.cpp +++ b/arbor/cable_cell_param.cpp @@ -114,15 +114,17 @@ std::vector<defaultable> cable_cell_parameter_set::serialize() const { return D; } -void decor::paint(region where, paintable what) { - paintings_.push_back({std::move(where), std::move(what)}); +decor& decor::paint(region where, paintable what) { + paintings_.emplace_back(std::move(where), std::move(what)); + return *this; } -void decor::place(locset where, placeable what, cell_tag_type label) { - placements_.push_back({std::move(where), std::move(what), std::move(label)}); +decor& decor::place(locset where, placeable what, cell_tag_type label) { + placements_.emplace_back(std::move(where), std::move(what), std::move(label)); + return *this; } -void decor::set_default(defaultable what) { +decor& decor::set_default(defaultable what) { std::visit( [this] (auto&& p) { using T = std::decay_t<decltype(p)>; @@ -158,6 +160,7 @@ void decor::set_default(defaultable what) { } }, what); + return *this; } } // namespace arb diff --git a/arbor/include/arbor/cable_cell_param.hpp b/arbor/include/arbor/cable_cell_param.hpp index 2c6cca399a0598375fcd26d86516d668ec2f480c..83a7e3deb3c1e49fafbea5545f12863bd2dd7129 100644 --- a/arbor/include/arbor/cable_cell_param.hpp +++ b/arbor/include/arbor/cable_cell_param.hpp @@ -311,9 +311,9 @@ public: const auto& placements() const {return placements_; } const auto& defaults() const {return defaults_; } - void paint(region, paintable); - void place(locset, placeable, cell_tag_type); - void set_default(defaultable); + decor& paint(region, paintable); + decor& place(locset, placeable, cell_tag_type); + decor& set_default(defaultable); }; ARB_ARBOR_API extern cable_cell_parameter_set neuron_parameter_defaults; diff --git a/doc/concepts/decor.rst b/doc/concepts/decor.rst index 03a255fcf794ee9c199f6112ef6218b517334686..630af679323239f29d0346b5fb43f4c597577564 100644 --- a/doc/concepts/decor.rst +++ b/doc/concepts/decor.rst @@ -31,6 +31,14 @@ Decorations are described by a **decor** object in Arbor. It provides facilities * setting properties defined over the whole cell; * descriptions of dynamics applied to regions and locsets. +.. note:: + + All methods on decor objects (``paint``, ``place``, and ``set_default``) + return a reference to the objects so you can chain them together. This saves + some repetition. You can break long statements over multiple lines, but in + Python this requires use of continuation lines ``\`` or wrapping the whole + expression into parentheses. + .. _cablecell-paint: Painted dynamics diff --git a/doc/tutorial/network_ring.rst b/doc/tutorial/network_ring.rst index 4e0ba577a4e57c00642be443a63c17e774d5b384..43c5d5a3af1bcf7e017fd3cf2cd27b910ed9b451 100644 --- a/doc/tutorial/network_ring.rst +++ b/doc/tutorial/network_ring.rst @@ -40,7 +40,7 @@ These locations will form the endpoints of the connections between the cells. .. literalinclude:: ../../python/example/network_ring.py :language: python - :lines: 48-51 + :lines: 48-58 After we've created a basic :py:class:`arbor.decor`, step **(3)** places a synapse with an exponential decay (``'expsyn'``) on the ``'synapse_site'``. The synapse is given the label ``'syn'``, which is later used to form :py:class:`arbor.connection` objects terminating *at* the cell. @@ -63,7 +63,7 @@ Step **(4)** places a spike detector at the ``'root'``. The detector is given th .. literalinclude:: ../../python/example/network_ring.py :language: python - :lines: 53-73 + :lines: 60-69 The recipe ********** @@ -107,7 +107,7 @@ Step **(11)** instantiates the recipe with 4 cells. .. literalinclude:: ../../python/example/network_ring.py :language: python - :lines: 76-124 + :lines: 74-122 The execution ************* @@ -143,7 +143,7 @@ Step **(15)** executes the simulation for a duration of 100 ms. .. literalinclude:: ../../python/example/network_ring.py :language: python - :lines: 126-138 + :lines: 124-136 The results *********** @@ -152,7 +152,7 @@ Step **(16)** prints the timestamps of the spikes: .. literalinclude:: ../../python/example/network_ring.py :language: python - :lines: 140-143 + :lines: 138-141 Step **(17)** generates a plot of the sampling data. :py:func:`arbor.simulation.samples` takes a ``handle`` of the probe we wish to examine. It returns a list @@ -164,7 +164,7 @@ It could have described a :term:`locset`.) .. literalinclude:: ../../python/example/network_ring.py :language: python - :lines: 145- + :lines: 143- Since we have created ``ncells`` cells, we have ``ncells`` traces. We should be seeing phase shifted traces, as the action potential propagated through the network. diff --git a/doc/tutorial/single_cell_detailed.rst b/doc/tutorial/single_cell_detailed.rst index 68c7b9c8e80a99549925e9f8ee0f3467812a5ded..c61ad38715d790d5df5a75a115f45eb604dec67c 100644 --- a/doc/tutorial/single_cell_detailed.rst +++ b/doc/tutorial/single_cell_detailed.rst @@ -95,13 +95,9 @@ to :ref:`Arbor's specifications <morph-formats>`). We can save the following in The morphology can then be loaded from ``single_cell_detailed.swc`` in the following way: -.. code-block:: python - - import arbor - - # (1) Read the morphology from an SWC file - - morph = arbor.load_swc_arbor("single_cell_detailed.swc") +.. literalinclude:: ../../python/example/single_cell_detailed.py + :language: python + :lines: 10-20 The label dictionary ^^^^^^^^^^^^^^^^^^^^ @@ -172,7 +168,7 @@ in the following way: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 29-32 + :lines: 30-31 This will generate the following regions when applied to the previously defined morphology: @@ -190,7 +186,7 @@ be done as follows: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 33-34 + :lines: 32-33 This will generate the following region when applied to the previously defined morphology: @@ -204,7 +200,7 @@ terminal points of the morphology. .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 38-40 + :lines: 35-37 This will generate the following **locsets** (sets of one or more locations) when applied to the previously defined morphology: @@ -221,7 +217,7 @@ previously defined "custom" region; and, separately, the terminal points which b .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 41-44 + :lines: 38-41 This will generate the following 2 locsets when applied to the previously defined morphology: @@ -254,7 +250,7 @@ step: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 46-53 + :lines: 48-52 We have set the default initial membrane voltage to -55 mV; the default initial temperature to 300 K; the default axial resistivity to 35.4 Ω·cm; and the default membrane @@ -273,8 +269,8 @@ We can override the default properties by *painting* new values on the relevant :meth:`arbor.decor.paint`. .. literalinclude:: ../../python/example/single_cell_detailed.py - :language: python - :lines: 55-57 + :language: python + :lines: 53-55 With the default and initial values taken care of, we now add some density mechanisms. Let's *paint* a *pas* density mechanism everywhere on the cell using the previously defined "all" region; an *hh* @@ -283,7 +279,7 @@ mechanism has a custom 'gbar' parameter. .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 8,59-62 + :lines: 8,56-59 The decor object is also used to *place* stimuli and spike detectors on the cell using :meth:`arbor.decor.place`. We place 3 current clamps of 2 nA on the "root" locset defined earlier, starting at time = 10, 30, 50 ms and @@ -293,7 +289,7 @@ in the recipe. .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 64-68 + :lines: 60-64 .. Note:: @@ -313,13 +309,13 @@ to be a single CV, and the rest of the morphology to be comprised of CVs with a .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 70-77 + :lines: 65-66 Finally, we create the cell. .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 79-81 + :lines: 68-70 The model ********* @@ -328,7 +324,7 @@ Having created the cell, we construct an :class:`arbor.single_cell_model`. .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 83-85 + :lines: 72-74 The global properties ^^^^^^^^^^^^^^^^^^^^^ @@ -370,7 +366,7 @@ model: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 87-91 + :lines: 76-80 We set the same properties as we did earlier when we were creating the *decor* of the cell, except for the initial membrane voltage, which is -65 mV as opposed to -55 mV. @@ -381,7 +377,7 @@ the "allen" catalogue. We can extend the default catalogue as follow: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 93-97 + :lines: 82-86 Now all three mechanisms in the *decor* object have been made available to the model. @@ -396,7 +392,7 @@ We can indicate the location we would like to probe using labels from the :class .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 99-103 + :lines: 88-92 The simulation ^^^^^^^^^^^^^^ @@ -405,7 +401,7 @@ The cell and model descriptions are now complete and we can run the simulation: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 105-107 + :lines: 94-102 The results ^^^^^^^^^^^ @@ -416,7 +412,7 @@ spikes on the cell from all spike detectors on the cell and saves the times at w .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 109-113 + :lines: 98-102 A more interesting result of the simulation is perhaps the output of the voltage probe previously placed on the "custom_terminal" locset. The model saves the output of the probes as [time, value] @@ -425,7 +421,7 @@ choose the any other library: .. literalinclude:: ../../python/example/single_cell_detailed.py :language: python - :lines: 5,6,114- + :lines: 5,6,104- The following plot is generated. The orange line is slightly delayed from the blue line, which is what we'd expect because branch 4 is longer than branch 3 of the morphology. We also see 3 spikes, diff --git a/doc/tutorial/single_cell_detailed_recipe.rst b/doc/tutorial/single_cell_detailed_recipe.rst index 451e28cda8a422dd318e668572d4a53aba897242..66f694ac31f7028211677d5dd85e3d17a5cd7f51 100644 --- a/doc/tutorial/single_cell_detailed_recipe.rst +++ b/doc/tutorial/single_cell_detailed_recipe.rst @@ -32,7 +32,7 @@ examine the recipe in detail: how to create one, and why it is needed. .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 84-120 + :lines: 74-114 Let's go through the recipe point by point. @@ -88,26 +88,22 @@ Now we can instantiate a ``single_recipe`` object. .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 123-124 + :lines: 113-114 The simulation ************** We have all we need to create a :class:`arbor.simulation` object. -.. literalinclude:: ../../python/example/single_cell_detailed_recipe.py - :language: python - :lines: 126-127 - Before we run the simulation, however, we need to register what results we expect once execution is over. This was handled by the :class:`arbor.single_cell_model` object in the original example. -We would like to get a list of the spikes on the cell during the runtime of the simulation, and we would like -to plot the voltage registered by the probe on the "custom_terminal" locset. - .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 129-133 + :lines: 116-123 + +We would like to get a list of the spikes on the cell during the runtime of the simulation, and we would like +to plot the voltage registered by the probe on the "custom_terminal" locset. The lines handling probe sampling warrant a second look. First, we declared :term:`probe_id` to be a :class:`arbor.cell_member`, with :class:`arbor.cell_member.gid` = 0 and :class:`arbor.cell_member.index` = 0. @@ -121,7 +117,7 @@ We can now run the simulation we just instantiated for a duration of 100 ms with .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 135-136 + :lines: 125-126 The results *********** @@ -133,13 +129,13 @@ We can print the times of the spikes: .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 138-142 + :lines: 128-132 The probe results, again, warrant some more explanation: .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 144-148 + :lines: 134-138 ``sim.samples()`` takes a ``handle`` of the probe we wish to examine. It returns a list of ``(data, meta)`` terms: ``data`` being the time and value series of the probed quantity; and @@ -152,7 +148,7 @@ We plot the results using pandas and seaborn as we did in the original example, .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py :language: python - :lines: 150- + :lines: 140- The following plot is generated. Identical to the plot of the original example. diff --git a/doc/tutorial/single_cell_recipe.rst b/doc/tutorial/single_cell_recipe.rst index c9e77771bed071c5ae2577f86a97d31c004cbea6..40d3233578e6bb5da4c6a07bfeadb58443608de4 100644 --- a/doc/tutorial/single_cell_recipe.rst +++ b/doc/tutorial/single_cell_recipe.rst @@ -47,7 +47,7 @@ models without cells are quite boring! .. literalinclude:: ../../python/example/single_cell_recipe.py :language: python - :lines: 28-63 + :lines: 31-61 Step **(4)** describes the recipe that will reflect our single cell model. @@ -77,7 +77,11 @@ Step **(4.6)** returns the properties that will be applied to all cells of that More methods can be overridden if your model requires that, see :class:`arbor.recipe` for options. -Step **(5)** instantiates the recipe. +Now we instantiate the recipe + +.. literalinclude:: ../../python/example/single_cell_recipe.py + :language: python + :lines: 64-67 The simulation -------------- @@ -95,7 +99,7 @@ The details of manual hardware configuration will be left for another tutorial. .. literalinclude:: ../../python/example/single_cell_recipe.py :language: python - :lines: 65-75 + :lines: 68-79 Step **(6)** instantiates the simulation. @@ -114,7 +118,7 @@ If we create the same analysis of the results we therefore expect the same resul .. literalinclude:: ../../python/example/single_cell_recipe.py :language: python - :lines: 77- + :lines: 80- Step **(8)** plots the measured potentials during the runtime of the simulation. Retrieving the sampled quantities is a little different, these have to be accessed diff --git a/example/dryrun/branch_cell.hpp b/example/dryrun/branch_cell.hpp index 9750c46c0170c85dfe18c7cfbc4564c0a4065ba5..cf208d6d2cd5aedb7e3a7e9204c3ceccd3064b35 100644 --- a/example/dryrun/branch_cell.hpp +++ b/example/dryrun/branch_cell.hpp @@ -104,34 +104,22 @@ arb::cable_cell branch_cell(arb::cell_gid_type gid, const cell_parameters& param dist_from_soma += l; } - arb::label_dict labels; - using arb::reg::tagged; + arb::label_dict labels; labels.set("soma", tagged(stag)); labels.set("dend", tagged(dtag)); - arb::decor decor; - - decor.paint("soma"_lab, arb::density("hh")); - decor.paint("dend"_lab, arb::density("pas")); - - decor.set_default(arb::axial_resistivity{100}); // [Ω·cm] - - // Add spike threshold detector at the soma. - decor.place(arb::mlocation{0,0}, arb::threshold_detector{10}, "detector"); - - // Add a synapse to the mid point of the first dendrite. - decor.place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "synapse"); + auto decor = arb::decor() + .set_default(arb::axial_resistivity{100}) // [Ω·cm] + .paint("soma"_lab, arb::density("hh")) // Add HH dynamics to soma. + .paint("dend"_lab, arb::density("pas")) // Leaky current everywhere else. + .place(arb::mlocation{0,0}, arb::threshold_detector{10}, "detector") // Add spike threshold detector at the soma. + .place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "synapse") // Add a synapse to the mid point of the first dendrite. + .set_default(arb::cv_policy_every_segment()); // Make a CV between every sample in the sample tree. // Add additional synapses that will not be connected to anything. for (unsigned i=1u; i<params.synapses; ++i) { decor.place(arb::mlocation{1, 0.5}, arb::synapse("expsyn"), "dummy_synapses"); } - - // Make a CV between every sample in the sample tree. - decor.set_default(arb::cv_policy_every_segment()); - - arb::cable_cell cell(arb::morphology(tree), labels, decor); - - return cell; + return arb::cable_cell{arb::morphology(tree), labels, decor}; } diff --git a/example/gap_junctions/gap_junctions.cpp b/example/gap_junctions/gap_junctions.cpp index 10b124d1e37251552dce0b4b51a128216fad46bb..2887cb50c848bc94c803b21bab4987fbbbb071f4 100644 --- a/example/gap_junctions/gap_junctions.cpp +++ b/example/gap_junctions/gap_junctions.cpp @@ -275,37 +275,21 @@ arb::cable_cell gj_cell(cell_gid_type gid, unsigned ncell, double stim_duration) double dend_rad = 3./2; // μm tree.append(0, {0,0,2*soma_rad, dend_rad}, {0,0,2*soma_rad+300, dend_rad}, 3); // dendrite - arb::decor decor; - - decor.set_default(arb::axial_resistivity{100}); // [Ω·cm] - decor.set_default(arb::membrane_capacitance{0.018}); // [F/m²] - - // Define the density channels and their parameters. - arb::mechanism_desc nax("nax"); - nax["gbar"] = 0.04; - nax["sh"] = 10; - - arb::mechanism_desc kdrmt("kdrmt"); - kdrmt["gbar"] = 0.0001; - - arb::mechanism_desc kamt("kamt"); - kamt["gbar"] = 0.004; - - arb::mechanism_desc pas("pas/e=-65.0"); - pas["g"] = 1.0/12000.0; - - // Paint density channels on all parts of the cell - decor.paint("(all)"_reg, arb::density{nax}); - decor.paint("(all)"_reg, arb::density{kdrmt}); - decor.paint("(all)"_reg, arb::density{kamt}); - decor.paint("(all)"_reg, arb::density{pas}); - - // Add a spike detector to the soma. - decor.place(arb::mlocation{0,0}, arb::threshold_detector{10}, "detector"); - - // Add two gap junction sites. - decor.place(arb::mlocation{0, 1}, arb::junction{"gj"}, "local_1"); - decor.place(arb::mlocation{0, 0}, arb::junction{"gj"}, "local_0"); + auto decor = arb::decor{} + .set_default(arb::axial_resistivity{100}) // [Ω·cm] + .set_default(arb::membrane_capacitance{0.018}) // [F/m²] + // Paint density channels on all parts of the cell + .paint("(all)"_reg, arb::density{"nax", {{"gbar", 0.04}, {"sh", 10}}}) + .paint("(all)"_reg, arb::density{"kdrmt", {{"gbar", 0.0001}}}) + .paint("(all)"_reg, arb::density{"kamt", {{"gbar", 0.004}}}) + .paint("(all)"_reg, arb::density{"pas/e=-65", {{"g", 1.0/12000.0}}}) + // Add a spike detector to the soma. + .place(arb::mlocation{0,0}, arb::threshold_detector{10}, "detector") + // Add two gap junction sites. + .place(arb::mlocation{0, 1}, arb::junction{"gj"}, "local_1") + .place(arb::mlocation{0, 0}, arb::junction{"gj"}, "local_0") + // Add a synapse to the mid point of the first dendrite. + .place(arb::mlocation{0, 0.5}, arb::synapse{"expsyn"}, "syn"); // Attach a stimulus to the first cell of the first group if (!gid) { @@ -313,9 +297,6 @@ arb::cable_cell gj_cell(cell_gid_type gid, unsigned ncell, double stim_duration) decor.place(arb::mlocation{0, 0.5}, stim, "stim"); } - // Add a synapse to the mid point of the first dendrite. - decor.place(arb::mlocation{0, 0.5}, arb::synapse{"expsyn"}, "syn"); - // Create the cell and set its electrical properties. return arb::cable_cell(tree, {}, decor); } diff --git a/example/generators/generators.cpp b/example/generators/generators.cpp index 9e72aff20af3da1ce9b854458f59d25efd57adc4..44c5f9c4fd122d9b4c4ce5432cfd19e3787eb1d0 100644 --- a/example/generators/generators.cpp +++ b/example/generators/generators.cpp @@ -59,13 +59,12 @@ public: arb::label_dict labels; labels.set("soma", arb::reg::tagged(1)); - arb::decor decor; - decor.paint("soma"_lab, arb::density("pas")); - - // Add one synapse at the soma. - // This synapse will be the target for all events, from both - // event_generators. - decor.place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "syn"); + auto decor = arb::decor{} + .paint("soma"_lab, arb::density("pas")) + // Add one synapse at the soma. + // This synapse will be the target for all events, from both + // event_generators. + .place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "syn"); return arb::cable_cell(tree, labels, decor); } diff --git a/example/lfp/CMakeLists.txt b/example/lfp/CMakeLists.txt index a3046c48121e81509f63aeb205eef364b559a3f9..a6dbabfd6e82bb188750bff2785ee177b93571cd 100644 --- a/example/lfp/CMakeLists.txt +++ b/example/lfp/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(lfp EXCLUDE_FROM_ALL lfp.cpp) add_dependencies(examples lfp) -target_link_libraries(lfp PRIVATE arbor ext-tinyopt) +target_link_libraries(lfp PRIVATE arbor arborio ext-tinyopt) file(COPY plot-lfp.py DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") diff --git a/example/lfp/lfp.cpp b/example/lfp/lfp.cpp index cb4f7ec26e223466b2f7267d00843276e5278714..95ead2c1529b1a9993eb79a311265810f3aa8dee 100644 --- a/example/lfp/lfp.cpp +++ b/example/lfp/lfp.cpp @@ -15,6 +15,8 @@ #include <arbor/util/any_ptr.hpp> #include <arbor/util/unique_any.hpp> +#include <arborio/label_parse.hpp> + using std::any; using arb::util::any_cast; using arb::util::any_ptr; @@ -22,6 +24,8 @@ using arb::util::unique_any; using arb::cell_gid_type; using arb::cell_member_type; +using namespace arborio::literals; + // Recipe represents one cable cell with one synapse, together with probes for total trans-membrane current, membrane voltage, // ionic current density, and synaptic conductance. A sequence of spikes are presented to the one synapse on the cell. @@ -78,22 +82,18 @@ private: // Apical dendrite, length 490 μm, radius 1 μm, with SWC tag 4. tree.append(soma_apex, {0, 0, 10, 1}, {0, 0, 500, 1}, 4); - auto dec = arb::decor(); - // Use NEURON defaults for reversal potentials, ion concentrations etc., but override ra, cm. - dec.set_default(axial_resistivity{100}); // [Ω·cm] - dec.set_default(membrane_capacitance{0.01}); // [F/m²] - - // Twenty CVs per branch on the dendrites (tag 4). - dec.set_default(cv_policy_fixed_per_branch(20, arb::reg::tagged(4))); - - // Add pas and hh mechanisms: - dec.paint(reg::tagged(1), density("hh")); // (default parameters) - dec.paint(reg::tagged(4), density("pas/e=-70.0")); - - // Add exponential synapse at centre of soma. - synapse_location_ = ls::on_components(0.5, reg::tagged(1)); - dec.place(synapse_location_, synapse("expsyn", {{"e", 0}, {"tau", 2}}), "syn"); - + synapse_location_ = "(on-components 0.5 (tag 1))"_ls; + auto dec = decor() + // Use NEURON defaults for reversal potentials, ion concentrations etc., but override ra, cm. + .set_default(axial_resistivity{100}) // [Ω·cm] + .set_default(membrane_capacitance{0.01}) // [F/m²] + // Twenty CVs per branch on the dendrites (tag 4). + .set_default(cv_policy_fixed_per_branch(20, arb::reg::tagged(4))) + // Add pas and hh mechanisms: + .paint("(tag 1)"_reg, density("hh")) // (default parameters) + .paint("(tag 4)"_reg, density("pas/e=-70.0")) + // Add exponential synapse at centre of soma. + .place(synapse_location_, synapse("expsyn", {{"e", 0}, {"tau", 2}}), "syn"); cell_ = cable_cell(tree, {}, dec); } }; diff --git a/example/probe-demo/probe-demo.cpp b/example/probe-demo/probe-demo.cpp index 0d27c111bd8b3a8b209717dc3dcbeff8904c42dc..16bcef344b83fa2b3910b7ec2887837e6fd66506 100644 --- a/example/probe-demo/probe-demo.cpp +++ b/example/probe-demo/probe-demo.cpp @@ -120,12 +120,11 @@ struct cable_recipe: public arb::recipe { arb::segment_tree tree; tree.append(arb::mnpos, {0, 0, 0, 0.5*diam}, {length, 0, 0, 0.5*diam}, 1); - arb::decor decor; - decor.paint(arb::reg::all(), arb::density("hh")); // HH mechanism over whole cell. - decor.place(arb::mlocation{0, 0.}, arb::i_clamp{1.}, "iclamp"); // Inject a 1 nA current indefinitely. - decor.place(arb::mlocation{0, 0.}, arb::synapse("expsyn"), "synapse1"); // a synapse - decor.place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "synapse2"); // another synapse - + auto decor = arb::decor{} + .paint(arb::reg::all(), arb::density("hh")) // HH mechanism over whole cell. + .place(arb::mlocation{0, 0.}, arb::i_clamp{1.}, "iclamp") // Inject a 1 nA current indefinitely. + .place(arb::mlocation{0, 0.}, arb::synapse("expsyn"), "synapse1") // a synapse + .place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "synapse2"); // another synapse return arb::cable_cell(tree, {}, decor); } diff --git a/example/ring/branch_cell.hpp b/example/ring/branch_cell.hpp index 527f26012c4a47dac2b285d25c66347f18054e55..503827c0c1559b01a30d21f354fd7c04a7e1a681 100644 --- a/example/ring/branch_cell.hpp +++ b/example/ring/branch_cell.hpp @@ -110,21 +110,13 @@ arb::cable_cell branch_cell(arb::cell_gid_type gid, const cell_parameters& param labels.set("soma", tagged(stag)); labels.set("dend", tagged(dtag)); - arb::decor decor; - - decor.paint("soma"_lab, arb::density("hh")); - decor.paint("dend"_lab, arb::density("pas")); - - decor.set_default(arb::axial_resistivity{100}); // [Ω·cm] - - // Add spike threshold detector at the soma. - decor.place(arb::mlocation{0,0}, arb::threshold_detector{10}, "detector"); - - // Add a synapse to the mid point of the first dendrite. - decor.place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "primary_syn"); - + auto decor = arb::decor{} + .paint("soma"_lab, arb::density("hh")) + .paint("dend"_lab, arb::density("pas")) + .set_default(arb::axial_resistivity{100}) // [Ω·cm] + .place(arb::mlocation{0,0}, arb::threshold_detector{10}, "detector") // Add spike threshold detector at the soma. + .place(arb::mlocation{0, 0.5}, arb::synapse("expsyn"), "primary_syn"); // Add a synapse to the mid point of the first dendrite. // Add additional synapses that will not be connected to anything. - if (params.synapses > 1) { decor.place(arb::ls::uniform("dend"_lab, 0, params.synapses - 2, gid), arb::synapse("expsyn"), "extra_syns"); } diff --git a/example/single/single.cpp b/example/single/single.cpp index 4befabcdc35a3ead4698cf3e3d1a71df164841ce..26afc366ee16654d8743518b4619f4372f833f00 100644 --- a/example/single/single.cpp +++ b/example/single/single.cpp @@ -65,17 +65,12 @@ struct single_recipe: public arb::recipe { dict.set("soma", tagged(1)); dict.set("dend", join(tagged(3), tagged(4), tagged(42))); - arb::decor decor; - - // Add HH mechanism to soma, passive channels to dendrites. - decor.paint("soma"_lab, arb::density("hh")); - decor.paint("dend"_lab, arb::density("pas")); - - // Add synapse to last branch. - - arb::cell_lid_type last_branch = morpho.num_branches()-1; - arb::mlocation end_last_branch = { last_branch, 1. }; - decor.place(end_last_branch, arb::synapse("exp2syn"), "synapse"); + auto decor = arb::decor{} + // Add HH mechanism to soma, passive channels to dendrites. + .paint("soma"_lab, arb::density("hh")) + .paint("dend"_lab, arb::density("pas")) + // Add synapse to last branch. + .place(arb::mlocation{ morpho.num_branches()-1, 1. }, arb::synapse("exp2syn"), "synapse"); return arb::cable_cell(morpho, dict, decor); } diff --git a/python/cells.cpp b/python/cells.cpp index 1020143d480613896d33e060e4e80c7fad092a21..ca4716f2ca22c1da7abf701e52c214753fcf19cd 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -657,6 +657,7 @@ void register_cells(pybind11::module& m) { if (cm) d.set_default(arb::membrane_capacitance{*cm}); if (rL) d.set_default(arb::axial_resistivity{*rL}); if (tempK) d.set_default(arb::temperature_K{*tempK}); + return d; }, pybind11::arg_v("Vm", pybind11::none(), "initial membrane voltage [mV]."), pybind11::arg_v("cm", pybind11::none(), "membrane capacitance [F/m²]."), @@ -674,9 +675,8 @@ void register_cells(pybind11::module& m) { if (ext_con) d.set_default(arb::init_ext_concentration{ion, *ext_con}); if (rev_pot) d.set_default(arb::init_reversal_potential{ion, *rev_pot}); if (diff) d.set_default(arb::ion_diffusivity{ion, *diff}); - if (auto m = maybe_method(method)) { - d.set_default(arb::ion_reversal_potential_method{ion, *m}); - } + if (auto m = maybe_method(method)) d.set_default(arb::ion_reversal_potential_method{ion, *m}); + return d; }, pybind11::arg_v("ion", "name of the ion species."), pybind11::arg_v("int_con", pybind11::none(), "initial internal concentration [mM]."), @@ -692,7 +692,7 @@ void register_cells(pybind11::module& m) { // Paint mechanisms. .def("paint", [](arb::decor& dec, const char* region, const arb::density& mechanism) { - dec.paint(arborio::parse_region_expression(region).unwrap(), mechanism); + return dec.paint(arborio::parse_region_expression(region).unwrap(), mechanism); }, "region"_a, "mechanism"_a, "Associate a density mechanism with a region.") @@ -705,7 +705,7 @@ void register_cells(pybind11::module& m) { // Paint membrane/static properties. .def("paint", [](arb::decor& dec, - const char* region, + const char* region, optional<double> Vm, optional<double> cm, optional<double> rL, optional<double> tempK) { @@ -714,6 +714,7 @@ void register_cells(pybind11::module& m) { if (cm) dec.paint(r, arb::membrane_capacitance{*cm}); if (rL) dec.paint(r, arb::axial_resistivity{*rL}); if (tempK) dec.paint(r, arb::temperature_K{*tempK}); + return dec; }, pybind11::arg_v("region", "the region label or description."), pybind11::arg_v("Vm", pybind11::none(), "initial membrane voltage [mV]."), @@ -731,6 +732,7 @@ void register_cells(pybind11::module& m) { if (ext_con) dec.paint(r, arb::init_ext_concentration{name, *ext_con}); if (rev_pot) dec.paint(r, arb::init_reversal_potential{name, *rev_pot}); if (diff) dec.paint(r, arb::ion_diffusivity{name, *diff}); + return dec; }, "region"_a, pybind11::kw_only(), "ion_name"_a, pybind11::arg_v("int_con", pybind11::none(), "Initial internal concentration [mM]"), @@ -771,11 +773,11 @@ void register_cells(pybind11::module& m) { "Add a voltage spike detector at each location in locations." "The group of spike detectors has the label 'label', used for forming connections between cells.") .def("discretization", - [](arb::decor& dec, const arb::cv_policy& p) { dec.set_default(p); }, + [](arb::decor& dec, const arb::cv_policy& p) { return dec.set_default(p); }, pybind11::arg_v("policy", "A cv_policy used to discretise the cell into compartments for simulation")) .def("discretization", [](arb::decor& dec, const std::string& p) { - dec.set_default(arborio::parse_cv_policy_expression(p).unwrap()); + return dec.set_default(arborio::parse_cv_policy_expression(p).unwrap()); }, pybind11::arg_v("policy", "An s-expression string representing a cv_policy used to discretise the " "cell into compartments for simulation")); diff --git a/python/example/dynamic-catalogue.py b/python/example/dynamic-catalogue.py index 67fc7725edcf8cfb342f6a8d4c979810b75666a4..64c74cc926e6009e310f464e7c2c3ffa31fd54cd 100644 --- a/python/example/dynamic-catalogue.py +++ b/python/example/dynamic-catalogue.py @@ -14,9 +14,7 @@ class recipe(arb.recipe): self.tree.append(arb.mnpos, (0, 0, 0, 10), (1, 0, 0, 10), 1) self.props = arb.neuron_cable_properties() self.props.catalogue = arb.load_catalogue(cat) - d = arb.decor() - d.paint("(all)", arb.density("dummy")) - d.set_property(Vm=0.0) + d = arb.decor().paint("(all)", "dummy").set_property(Vm=0.0) self.cell = arb.cable_cell(self.tree, arb.label_dict(), d) def global_properties(self, _): diff --git a/python/example/gap_junctions.py b/python/example/gap_junctions.py index 2541abd0098d811c733b6ed4afbf8c3ec302282a..2f93e22424a5e948bad345f241033368ded038bf 100644 --- a/python/example/gap_junctions.py +++ b/python/example/gap_junctions.py @@ -32,32 +32,30 @@ 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().add_swc_tags() - - # Mark location for synapse site at midpoint of dendrite (branch 0 = soma + dendrite) - labels["synapse_site"] = "(location 0 0.6)" - - # Gap junction site at connection point of soma and dendrite - labels["gj_site"] = "(location 0 0.2)" - - # Label root of the tree - labels["root"] = "(root)" + labels = arbor.label_dict( + { + # Mark location for synapse site at midpoint of dendrite (branch 0 soma + dendrite) + "synapse_site": "(location 0 0.6)", + # Gap junction site at connection point of soma and dendrite + "gj_site": "(location 0 0.2)", + # Label root of the tree + "root": "(root)", + } + ).add_swc_tags() # Paint dynamics onto the cell, hh on soma and passive properties on dendrite - decor = arbor.decor() - decor.paint('"soma"', arbor.density("hh")) - decor.paint('"dend"', arbor.density("pas")) - - # Attach one synapse and gap junction each on their labeled sites - decor.place('"synapse_site"', arbor.synapse("expsyn"), "syn") - decor.place('"gj_site"', arbor.junction("gj"), "gj") - - # Attach spike detector to cell root - decor.place('"root"', arbor.spike_detector(-10), "detector") - - cell = arbor.cable_cell(tree, labels, decor) + decor = ( + arbor.decor() + .paint('"soma"', arbor.density("hh")) + .paint('"dend"', arbor.density("pas")) + # Attach one synapse and gap junction each on their labeled sites + .place('"synapse_site"', arbor.synapse("expsyn"), "syn") + .place('"gj_site"', arbor.junction("gj"), "gj") + # Attach spike detector to cell root + .place('"root"', arbor.spike_detector(-10), "detector") + ) - return cell + return arbor.cable_cell(tree, labels, decor) # Create a recipe that generates connected chains of cells diff --git a/python/example/network_ring.py b/python/example/network_ring.py index 692f44433cd719f5d9fc02afd40897b5d517d477..6013131001b254aa096ddf2e5d536d5bc4c99e92 100755 --- a/python/example/network_ring.py +++ b/python/example/network_ring.py @@ -46,31 +46,29 @@ def make_cable_cell(gid): ) # Associate labels to tags - labels = arbor.label_dict() - labels["soma"] = "(tag 1)" - labels["dend"] = "(tag 3)" - - # (2) Mark location for synapse at the midpoint of branch 1 (the first dendrite). - labels["synapse_site"] = "(location 1 0.5)" - # Mark the root of the tree. - labels["root"] = "(root)" + labels = arbor.label_dict( + { + "soma": "(tag 1)", + "dend": "(tag 3)", + # (2) Mark location for synapse at the midpoint of branch 1 (the first dendrite). + "synapse_site": "(location 1 0.5)", + # Mark the root of the tree. + "root": "(root)", + } + ) # (3) Create a decor and a cable_cell - decor = arbor.decor() - - # Put hh dynamics on soma, and passive properties on the dendrites. - decor.paint('"soma"', arbor.density("hh")) - decor.paint('"dend"', arbor.density("pas")) - - # (4) Attach a single synapse. - decor.place('"synapse_site"', arbor.synapse("expsyn"), "syn") - - # Attach a spike detector with threshold of -10 mV. - decor.place('"root"', arbor.spike_detector(-10), "detector") - - cell = arbor.cable_cell(tree, labels, decor) + decor = ( + arbor.decor() + # Put hh dynamics on soma, and passive properties on the dendrites. + .paint('"soma"', arbor.density("hh")).paint('"dend"', arbor.density("pas")) + # (4) Attach a single synapse. + .place('"synapse_site"', arbor.synapse("expsyn"), "syn") + # Attach a spike detector with threshold of -10 mV. + .place('"root"', arbor.spike_detector(-10), "detector") + ) - return cell + return arbor.cable_cell(tree, labels, decor) # (5) Create a recipe that generates a network of connected cells. diff --git a/python/example/network_ring_mpi.py b/python/example/network_ring_mpi.py index 2ac4f014380f19339a35be803c6d029201264d04..6892a498eebd29dcee04a603d1a1a8869d90511c 100644 --- a/python/example/network_ring_mpi.py +++ b/python/example/network_ring_mpi.py @@ -48,31 +48,29 @@ def make_cable_cell(gid): ) # Associate labels to tags - labels = arbor.label_dict() - labels["soma"] = "(tag 1)" - labels["dend"] = "(tag 3)" - - # (2) Mark location for synapse at the midpoint of branch 1 (the first dendrite). - labels["synapse_site"] = "(location 1 0.5)" - # Mark the root of the tree. - labels["root"] = "(root)" + labels = arbor.label_dict( + { + "soma": "(tag 1)", + "dend": "(tag 3)", + # (2) Mark location for synapse at the midpoint of branch 1 (the first dendrite). + "synapse_site": "(location 1 0.5)", + # Mark the root of the tree. + "root": "(root)", + } + ) # (3) Create a decor and a cable_cell - decor = arbor.decor() - - # Put hh dynamics on soma, and passive properties on the dendrites. - decor.paint('"soma"', arbor.density("hh")) - decor.paint('"dend"', arbor.density("pas")) - - # (4) Attach a single synapse. - decor.place('"synapse_site"', arbor.synapse("expsyn"), "syn") - - # Attach a spike detector with threshold of -10 mV. - decor.place('"root"', arbor.spike_detector(-10), "detector") - - cell = arbor.cable_cell(tree, labels, decor) + decor = ( + arbor.decor() + # Put hh dynamics on soma, and passive properties on the dendrites. + .paint('"soma"', arbor.density("hh")).paint('"dend"', arbor.density("pas")) + # (4) Attach a single synapse. + .place('"synapse_site"', arbor.synapse("expsyn"), "syn") + # Attach a spike detector with threshold of -10 mV. + .place('"root"', arbor.spike_detector(-10), "detector") + ) - return cell + return arbor.cable_cell(tree, labels, decor) # (5) Create a recipe that generates a network of connected cells. diff --git a/python/example/network_two_cells_gap_junctions.py b/python/example/network_two_cells_gap_junctions.py index 54fa53c0810a2a741afebc04a329e0a6be3d3dbf..95fd830ec4b4ac0ac55f42637fc6c4fc613e22ae 100755 --- a/python/example/network_two_cells_gap_junctions.py +++ b/python/example/network_two_cells_gap_junctions.py @@ -82,15 +82,15 @@ class TwoCellsWithGapJunction(arbor.recipe): labels = arbor.label_dict({"cell": "(tag 1)", "gj_site": "(location 0 0.5)"}) - decor = arbor.decor() - decor.set_property(Vm=self.Vms[gid]) - decor.set_property(cm=self.cm) - decor.set_property(rL=self.rL) - - # add a gap junction mechanism at the "gj_site" location and label that specific mechanism on that location "gj_label" - junction_mech = arbor.junction("gj", {"g": self.gj_g}) - decor.place('"gj_site"', junction_mech, "gj_label") - decor.paint('"cell"', arbor.density(f"pas/e={self.Vms[gid]}", {"g": self.g})) + decor = ( + arbor.decor() + .set_property(Vm=self.Vms[gid]) + .set_property(cm=self.cm) + .set_property(rL=self.rL) + # add a gap junction mechanism at the "gj_site" location and label that specific mechanism on that location "gj_label" + .place('"gj_site"', arbor.junction("gj", {"g": self.gj_g}), "gj_label") + .paint('"cell"', arbor.density(f"pas/e={self.Vms[gid]}", {"g": self.g})) + ) if self.cv_policy_max_extent is not None: policy = arbor.cv_policy_max_extent(self.cv_policy_max_extent) @@ -101,14 +101,15 @@ class TwoCellsWithGapJunction(arbor.recipe): return arbor.cable_cell(tree, labels, decor) def gap_junctions_on(self, gid): - assert gid in [0, 1] - # create a bidirectional gap junction from cell 0 at label "gj_label" to cell 1 at label "gj_label" and back. - return [ - arbor.gap_junction_connection( - (1 if gid == 0 else 0, "gj_label"), "gj_label", 1 - ) - ] + + if gid == 0: + tgt = 1 + elif gid == 1: + tgt = 0 + else: + raise RuntimeError("Invalid GID for example.") + return [arbor.gap_junction_connection((tgt, "gj_label"), "gj_label", 1)] if __name__ == "__main__": diff --git a/python/example/probe_lfpykit.py b/python/example/probe_lfpykit.py index 9f99bcd7ce0fba18d9679094bb34e8af4ad381a6..10e769345a871c2276a5ff2d8ecc975ade1c932c 100644 --- a/python/example/probe_lfpykit.py +++ b/python/example/probe_lfpykit.py @@ -77,22 +77,20 @@ def make_cable_cell(morphology, clamp_location): labels = arbor.label_dict() # decor - decor = arbor.decor() - - # set initial voltage, temperature, axial resistivity, membrane capacitance - decor.set_property( - Vm=-65, # Initial membrane voltage (mV) - tempK=300, # Temperature (Kelvin) - rL=10000, # Axial resistivity (Ω cm) - cm=0.01, # Membrane capacitance (F/m**2) + decor = ( + arbor.decor() + # set initial voltage, temperature, axial resistivity, membrane capacitance + .set_property( + Vm=-65, # Initial membrane voltage (mV) + tempK=300, # Temperature (Kelvin) + rL=10000, # Axial resistivity (Ω cm) + cm=0.01, # Membrane capacitance (F/m**2) + ) + # set passive mechanism all over + # passive mech w. leak reversal potential (mV) + .paint("(all)", arbor.density("pas/e=-65", {"g": 0.0001})) ) - # set passive mechanism all over - # passive mech w. leak reversal potential (mV) - pas = arbor.mechanism("pas/e=-65") - pas.set("g", 0.0001) # leak conductivity (S/cm2) - decor.paint("(all)", arbor.density(pas)) - # set number of CVs per branch policy = arbor.cv_policy_fixed_per_branch(cvs_per_branch) decor.discretization(policy) diff --git a/python/example/single_cell_cable.py b/python/example/single_cell_cable.py index adf2e4e7bbb775a3e63321d01fea7d23ffc7323c..8cb5d4cfdb3bb0e61e5eea34dd89ce0356f6b6d7 100755 --- a/python/example/single_cell_cable.py +++ b/python/example/single_cell_cable.py @@ -92,19 +92,17 @@ class Cable(arbor.recipe): labels = arbor.label_dict({"cable": "(tag 1)", "start": "(location 0 0)"}) - decor = arbor.decor() - decor.set_property(Vm=self.Vm) - decor.set_property(cm=self.cm) - decor.set_property(rL=self.rL) - - decor.paint('"cable"', arbor.density(f"pas/e={self.Vm}", {"g": self.g})) - - decor.place( - '"start"', - arbor.iclamp( - self.stimulus_start, self.stimulus_duration, self.stimulus_amplitude - ), - "iclamp", + decor = ( + arbor.decor() + .set_property(Vm=self.Vm, cm=self.cm, rL=self.rL) + .paint('"cable"', arbor.density(f"pas/e={self.Vm}", {"g": self.g})) + .place( + '"start"', + arbor.iclamp( + self.stimulus_start, self.stimulus_duration, self.stimulus_amplitude + ), + "iclamp", + ) ) policy = arbor.cv_policy_max_extent(self.cv_policy_max_extent) diff --git a/python/example/single_cell_detailed.py b/python/example/single_cell_detailed.py index afff91736d4ec9c715d6c67bd8f1ca43d7526632..b9f85fd99850f4dd3702850a899fc4cc7c188613 100755 --- a/python/example/single_cell_detailed.py +++ b/python/example/single_cell_detailed.py @@ -21,60 +21,49 @@ morph = arbor.load_swc_arbor(filename) # (2) Create and populate the label dictionary. -# Pre-defined SWC regions -labels = arbor.label_dict().add_swc_tags() -# Regions: - -# 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 "apic" and "gt_1.5" -labels["custom"] = '(join (region "apic") (region "gt_1.5"))' - -# Locsets: - -# Add a labels for the root of the morphology and all the terminal points -labels["root"] = "(root)" -labels["terminal"] = "(terminal)" -# Add a label for the terminal locations in the "custom" region: -labels["custom_terminal"] = '(restrict (locset "terminal") (region "custom"))' -# Add a label for the terminal locations in the "axon" region: -labels["axon_terminal"] = '(restrict (locset "terminal") (region "axon"))' +labels = arbor.label_dict( + { + # Regions: + # Add a label for a region that includes the whole morphology + "all": "(all)", + # Add a label for the parts of the morphology with radius greater than 1.5 μm. + "gt_1.5": '(radius-ge (region "all") 1.5)', + # Join regions "apic" and "gt_1.5" + "custom": '(join (region "apic") (region "gt_1.5"))', + # Locsets: + # Add a labels for the root of the morphology and all the terminal points + "root": "(root)", + "terminal": "(terminal)", + # Add a label for the terminal locations in the "custom" region: + "custom_terminal": '(restrict (locset "terminal") (region "custom"))', + # Add a label for the terminal locations in the "axon" region: + "axon_terminal": '(restrict (locset "terminal") (region "axon"))', + } +).add_swc_tags() # (3) Create and populate the decor. +# NB. This can be written more compactly using method chaining decor = arbor.decor() - # Set the default properties of the cell (this overrides the model defaults). decor.set_property(Vm=-55) decor.set_ion("na", int_con=10, ext_con=140, rev_pot=50, method="nernst/na") decor.set_ion("k", int_con=54.4, ext_con=2.5, rev_pot=-77) - # Override the cell defaults. decor.paint('"custom"', tempK=270) decor.paint('"soma"', Vm=-50) - # Paint density mechanisms. decor.paint('"all"', density("pas")) decor.paint('"custom"', density("hh")) decor.paint('"dend"', density("Ih", {"gbar": 0.001})) - # Place stimuli and spike detectors. decor.place('"root"', arbor.iclamp(10, 1, current=2), "iclamp0") decor.place('"root"', arbor.iclamp(30, 1, current=2), "iclamp1") decor.place('"root"', arbor.iclamp(50, 1, current=2), "iclamp2") decor.place('"axon_terminal"', arbor.spike_detector(-10), "detector") - -# Single CV for the "soma" region -soma_policy = arbor.cv_policy_single('"soma"') -# Single CV for the "soma" region -dflt_policy = arbor.cv_policy_max_extent(1.0) -# default policy everywhere except the soma -policy = dflt_policy | soma_policy -# Set cv_policy -decor.discretization(policy) +# Set discretisation: Soma as one CV, 1um everywhere else +decor.discretization('(replace (single (region "soma")) (max-extent 1.0))') # (4) Create the cell. diff --git a/python/example/single_cell_detailed_recipe.py b/python/example/single_cell_detailed_recipe.py index 86aa9ce49f59c0ecf7b86c7a927e3f6d5fa9a065..e13f8a85b38e12abe264203d7f66cfb1587bfd09 100644 --- a/python/example/single_cell_detailed_recipe.py +++ b/python/example/single_cell_detailed_recipe.py @@ -21,60 +21,50 @@ morph = arbor.load_swc_arbor(filename) # (2) Create and populate the label dictionary. -# Label dict, with Pre-defined labels soma, axon, dend, and apic -labels = arbor.label_dict().add_swc_tags() - -# Regions: - -# 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 "apic" and "gt_1.5" -labels["custom"] = '(join (region "apic") (region "gt_1.5"))' - -# Locsets: - -# Add a labels for the root of the morphology and all the terminal points -labels["root"] = "(root)" -labels["terminal"] = "(terminal)" -# Add a label for the terminal locations in the "custom" region: -labels["custom_terminal"] = '(restrict (locset "terminal") (region "custom"))' -# Add a label for the terminal locations in the "axon" region: -labels["axon_terminal"] = '(restrict (locset "terminal") (region "axon"))' +labels = arbor.label_dict( + { + # Regions: + # Add a label for a region that includes the whole morphology + "all": "(all)", + # Add a label for the parts of the morphology with radius greater than 1.5 μm. + "gt_1.5": '(radius-ge (region "all") 1.5)', + # Join regions "apic" and "gt_1.5" + "custom": '(join (region "apic") (region "gt_1.5"))', + # Locsets: + # Add a labels for the root of the morphology and all the terminal points + "root": "(root)", + "terminal": "(terminal)", + # Add a label for the terminal locations in the "custom" region: + "custom_terminal": '(restrict (locset "terminal") (region "custom"))', + # Add a label for the terminal locations in the "axon" region: + "axon_terminal": '(restrict (locset "terminal") (region "axon"))', + } +).add_swc_tags() # Add SWC pre-defined regions # (3) Create and populate the decor. -decor = arbor.decor() - -# Set the default properties of the cell (this overrides the model defaults). -decor.set_property(Vm=-55) -decor.set_ion("na", int_con=10, ext_con=140, rev_pot=50, method="nernst/na") -decor.set_ion("k", int_con=54.4, ext_con=2.5, rev_pot=-77) - -# Override the cell defaults. -decor.paint('"custom"', tempK=270) -decor.paint('"soma"', Vm=-50) - -# Paint density mechanisms. -decor.paint('"all"', density("pas")) -decor.paint('"custom"', density("hh")) -decor.paint('"dend"', density("Ih", {"gbar": 0.001})) - -# Place stimuli and spike detectors. -decor.place('"root"', arbor.iclamp(10, 1, current=2), "iclamp0") -decor.place('"root"', arbor.iclamp(30, 1, current=2), "iclamp1") -decor.place('"root"', arbor.iclamp(50, 1, current=2), "iclamp2") -decor.place('"axon_terminal"', arbor.spike_detector(-10), "detector") - -# Single CV for the "soma" region -soma_policy = arbor.cv_policy_single('"soma"') -# Single CV for the "soma" region -dflt_policy = arbor.cv_policy_max_extent(1.0) -# default policy everywhere except the soma -policy = dflt_policy | soma_policy -# Set cv_policy -decor.discretization(policy) +decor = ( + arbor.decor() + # Set the default properties of the cell (this overrides the model defaults). + .set_property(Vm=-55) + .set_ion("na", int_con=10, ext_con=140, rev_pot=50, method="nernst/na") + .set_ion("k", int_con=54.4, ext_con=2.5, rev_pot=-77) + # Override the cell defaults. + .paint('"custom"', tempK=270) + .paint('"soma"', Vm=-50) + # Paint density mechanisms. + .paint('"all"', density("pas")) + .paint('"custom"', density("hh")) + .paint('"dend"', density("Ih", {"gbar": 0.001})) + # Place stimuli and spike detectors. + .place('"root"', arbor.iclamp(10, 1, current=2), "iclamp0") + .place('"root"', arbor.iclamp(30, 1, current=2), "iclamp1") + .place('"root"', arbor.iclamp(50, 1, current=2), "iclamp2") + .place('"axon_terminal"', arbor.spike_detector(-10), "detector") + # Set discretisation: Soma as one CV, 1um everywhere else + .discretization('(replace (single (region "soma")) (max-extent 1.0))') +) + # (4) Create the cell. diff --git a/python/example/single_cell_model.py b/python/example/single_cell_model.py index 9510178248812b67b955e1214ace4052e9fc8487..28d52b1c423b75e69605889de8c9b21b691a4371 100755 --- a/python/example/single_cell_model.py +++ b/python/example/single_cell_model.py @@ -13,11 +13,14 @@ tree.append(arbor.mnpos, arbor.mpoint(-3, 0, 0, 3), arbor.mpoint(3, 0, 0, 3), ta labels = arbor.label_dict({"soma": "(tag 1)", "midpoint": "(location 0 0.5)"}) # (3) Create and set up a decor object -decor = arbor.decor() -decor.set_property(Vm=-40) -decor.paint('"soma"', arbor.density("hh")) -decor.place('"midpoint"', arbor.iclamp(10, 2, 0.8), "iclamp") -decor.place('"midpoint"', arbor.spike_detector(-10), "detector") + +decor = ( + arbor.decor() + .set_property(Vm=-40) + .paint('"soma"', arbor.density("hh")) + .place('"midpoint"', arbor.iclamp(10, 2, 0.8), "iclamp") + .place('"midpoint"', arbor.spike_detector(-10), "detector") +) # (4) Create cell and the single cell model based on it cell = arbor.cable_cell(tree, labels, decor) diff --git a/python/example/single_cell_nml.py b/python/example/single_cell_nml.py index 7082cd6a293108e173ebcb99c65b80892d7703bd..7e0f63fec817b36b2f78ff92c252553a3bab8da5 100755 --- a/python/example/single_cell_nml.py +++ b/python/example/single_cell_nml.py @@ -27,53 +27,46 @@ morpho_segments = morpho_data.segments() morpho_named = morpho_data.named_segments() morpho_groups = morpho_data.groups() -# Create new label dict add to it all the NeuroML dictionaries. -labels = arbor.label_dict() +# Create new label dict with some locsets. +labels = arbor.label_dict( + { + "stim_site": "(location 1 0.5)", # site for the stimulus, in the middle of branch 1. + "axon_end": '(restrict (terminal) (region "axon"))', # end of the axon. + "root": "(root)", # the start of the soma in this morphology is at the root of the cell. + } +) +# Add to it all the NeuroML dictionaries. labels.append(morpho_segments) labels.append(morpho_named) labels.append(morpho_groups) -# Add locsets to the label dictionary. -labels[ - "stim_site" -] = "(location 1 0.5)" # site for the stimulus, in the middle of branch 1. -labels["axon_end"] = '(restrict (terminal) (region "axon"))' # end of the axon. -labels[ - "root" -] = "(root)" # the start of the soma in this morphology is at the root of the cell. - # Optional: print out the regions and locsets available in the label dictionary. print("Label dictionary regions: ", labels.regions, "\n") print("Label dictionary locsets: ", labels.locsets, "\n") -decor = arbor.decor() - -# Set initial membrane potential to -55 mV -decor.set_property(Vm=-55) -# Use Nernst to calculate reversal potential for calcium. -decor.set_ion("ca", method=mech("nernst/x=ca")) -# decor.set_ion('ca', method='nernst/x=ca') -# hh mechanism on the soma and axon. -decor.paint('"soma"', arbor.density("hh")) -decor.paint('"axon"', arbor.density("hh")) -# pas mechanism the dendrites. -decor.paint('"dend"', arbor.density("pas")) -# Increase resistivity on dendrites. -decor.paint('"dend"', rL=500) -# Attach stimuli that inject 4 nA current for 1 ms, starting at 3 and 8 ms. -decor.place('"root"', arbor.iclamp(10, 1, current=5), "iclamp0") -decor.place('"stim_site"', arbor.iclamp(3, 1, current=0.5), "iclamp1") -decor.place('"stim_site"', arbor.iclamp(10, 1, current=0.5), "iclamp2") -decor.place('"stim_site"', arbor.iclamp(8, 1, current=4), "iclamp3") -# Detect spikes at the soma with a voltage threshold of -10 mV. -decor.place('"axon_end"', arbor.spike_detector(-10), "detector") - -# Create the policy used to discretise the cell into CVs. -# Use a single CV for the soma, and CVs of maximum length 1 μm elsewhere. -soma_policy = arbor.cv_policy_single('"soma"') -dflt_policy = arbor.cv_policy_max_extent(1.0) -policy = dflt_policy | soma_policy -decor.discretization(policy) +decor = ( + arbor.decor() + # Set initial membrane potential to -55 mV + .set_property(Vm=-55) + # Use Nernst to calculate reversal potential for calcium. + .set_ion("ca", method=mech("nernst/x=ca")) + # hh mechanism on the soma and axon. + .paint('"soma"', arbor.density("hh")) + .paint('"axon"', arbor.density("hh")) + # pas mechanism the dendrites. + .paint('"dend"', arbor.density("pas")) + # Increase resistivity on dendrites. + .paint('"dend"', rL=500) + # Attach stimuli that inject 4 nA current for 1 ms, starting at 3 and 8 ms. + .place('"root"', arbor.iclamp(10, 1, current=5), "iclamp0") + .place('"stim_site"', arbor.iclamp(3, 1, current=0.5), "iclamp1") + .place('"stim_site"', arbor.iclamp(10, 1, current=0.5), "iclamp2") + .place('"stim_site"', arbor.iclamp(8, 1, current=4), "iclamp3") + # Detect spikes at the soma with a voltage threshold of -10 mV. + .place('"axon_end"', arbor.spike_detector(-10), "detector") + # Set discretisation: Soma as one CV, 1um everywhere else + .discretization('(replace (single (region "soma")) (max-extent 1.0))') +) # Combine morphology with region and locset definitions to make a cable cell. cell = arbor.cable_cell(morpho, labels, decor) diff --git a/python/example/single_cell_recipe.py b/python/example/single_cell_recipe.py index 1a2f0334b370270e038b45f250050e91ae022182..6e12fd546da5f99c28dee13a6dac92d8e5cf498d 100644 --- a/python/example/single_cell_recipe.py +++ b/python/example/single_cell_recipe.py @@ -18,11 +18,14 @@ labels = arbor.label_dict({"soma": "(tag 1)", "midpoint": "(location 0 0.5)"}) # (3) Create cell and set properties -decor = arbor.decor() -decor.set_property(Vm=-40) -decor.paint('"soma"', arbor.density("hh")) -decor.place('"midpoint"', arbor.iclamp(10, 2, 0.8), "iclamp") -decor.place('"midpoint"', arbor.spike_detector(-10), "detector") +decor = ( + arbor.decor() + .set_property(Vm=-40) + .paint('"soma"', arbor.density("hh")) + .place('"midpoint"', arbor.iclamp(10, 2, 0.8), "iclamp") + .place('"midpoint"', arbor.spike_detector(-10), "detector") +) + cell = arbor.cable_cell(tree, labels, decor) # (4) Define a recipe for a single cell and set of probes upon it. @@ -31,30 +34,30 @@ cell = arbor.cable_cell(tree, labels, decor) class single_recipe(arbor.recipe): + # (4.1) The base class constructor must be called first, to ensure that + # all memory in the wrapped C++ class is initialized correctly. def __init__(self): - # (4.1) The base C++ class constructor must be called first, to ensure that - # all memory in the C++ class is initialized correctly. arbor.recipe.__init__(self) self.the_props = arbor.neuron_cable_properties() + # (4.2) Override the num_cells method def num_cells(self): - # (4.2) Override the num_cells method return 1 + # (4.3) Override the cell_kind method def cell_kind(self, gid): - # (4.3) Override the cell_kind method return arbor.cell_kind.cable + # (4.4) Override the cell_description method def cell_description(self, gid): - # (4.4) Override the cell_description method return cell + # (4.5) Override the probes method with a voltage probe located on "midpoint" def probes(self, gid): - # (4.5) Override the probes method with a voltage probe located on "midpoint" return [arbor.cable_probe_membrane_voltage('"midpoint"')] + # (4.6) Override the global_properties method def global_properties(self, kind): - # (4.6) Override the global_properties method return self.the_props diff --git a/python/example/single_cell_stdp.py b/python/example/single_cell_stdp.py index 8c2d443c86730def84b92c52473ec1f693cb7aa4..4330b9c269bec0659b0cc37423a4d040b14bdfb6 100755 --- a/python/example/single_cell_stdp.py +++ b/python/example/single_cell_stdp.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 import arbor -import numpy -import pandas -import seaborn # You may have to pip install these. +import numpy as np +import pandas as pd +import seaborn as sns # You may have to pip install these. class single_recipe(arbor.recipe): @@ -28,27 +28,25 @@ class single_recipe(arbor.recipe): labels = arbor.label_dict({"soma": "(tag 1)", "center": "(location 0 0.5)"}) - decor = arbor.decor() - decor.set_property(Vm=-40) - decor.paint("(all)", arbor.density("hh")) - - decor.place('"center"', arbor.spike_detector(-10), "detector") - decor.place('"center"', arbor.synapse("expsyn"), "synapse") - - mech = arbor.mechanism("expsyn_stdp") - mech.set("max_weight", 1.0) - syn = arbor.synapse(mech) - - decor.place('"center"', syn, "stpd_synapse") - - cell = arbor.cable_cell(tree, labels, decor) + decor = ( + arbor.decor() + .set_property(Vm=-40) + .paint("(all)", arbor.density("hh")) + .place('"center"', arbor.spike_detector(-10), "detector") + .place('"center"', arbor.synapse("expsyn"), "synapse") + .place( + '"center"', + arbor.synapse("expsyn_stdp", {"max_weight": 1.0}), + "stpd_synapse", + ) + ) - return cell + return arbor.cable_cell(tree, labels, decor) def event_generators(self, gid): """two stimuli: one that makes the cell spike, the other to monitor STDP""" - stimulus_times = numpy.linspace(50, 500, self.n_pairs) + stimulus_times = np.linspace(50, 500, self.n_pairs) # strong enough stimulus spike = arbor.event_generator( @@ -63,12 +61,15 @@ class single_recipe(arbor.recipe): return [spike, stdp] def probes(self, gid): + def mk(w): + return arbor.cable_probe_point_state(1, "expsyn_stdp", w) + return [ arbor.cable_probe_membrane_voltage('"center"'), - arbor.cable_probe_point_state(1, "expsyn_stdp", "g"), - arbor.cable_probe_point_state(1, "expsyn_stdp", "apost"), - arbor.cable_probe_point_state(1, "expsyn_stdp", "apre"), - arbor.cable_probe_point_state(1, "expsyn_stdp", "weight_plastic"), + mk("g"), + mk("apost"), + mk("apre"), + mk("weight_plastic"), ] def global_properties(self, kind): @@ -83,40 +84,33 @@ def run(dT, n_pairs=1, do_plots=False): sim.record(arbor.spike_recording.all) reg_sched = arbor.regular_schedule(0.1) - handle_mem = sim.sample((0, 0), reg_sched) - handle_g = sim.sample((0, 1), reg_sched) - handle_apost = sim.sample((0, 2), reg_sched) - handle_apre = sim.sample((0, 3), reg_sched) - handle_weight_plastic = sim.sample((0, 4), reg_sched) + handles = { + "U": sim.sample((0, 0), reg_sched), + "g": sim.sample((0, 1), reg_sched), + "apost": sim.sample((0, 2), reg_sched), + "apre": sim.sample((0, 3), reg_sched), + "weight_plastic": sim.sample((0, 4), reg_sched), + } sim.run(tfinal=600) if do_plots: print("Plotting detailed results ...") - - for (handle, var) in [ - (handle_mem, "U"), - (handle_g, "g"), - (handle_apost, "apost"), - (handle_apre, "apre"), - (handle_weight_plastic, "weight_plastic"), - ]: - + for var, handle in handles.items(): data, meta = sim.samples(handle)[0] - df = pandas.DataFrame({"t/ms": data[:, 0], var: data[:, 1]}) - seaborn.relplot(data=df, kind="line", x="t/ms", y=var, ci=None).savefig( - "single_cell_stdp_result_{}.svg".format(var) + df = pd.DataFrame({"t/ms": data[:, 0], var: data[:, 1]}) + sns.relplot(data=df, kind="line", x="t/ms", y=var, ci=None).savefig( + f"single_cell_stdp_result_{var}.svg" ) - weight_plastic, meta = sim.samples(handle_weight_plastic)[0] - + weight_plastic, _ = sim.samples(handles["weight_plastic"])[0] return weight_plastic[:, 1][-1] -data = numpy.array([(dT, run(dT)) for dT in numpy.arange(-20, 20, 0.5)]) -df = pandas.DataFrame({"t/ms": data[:, 0], "dw": data[:, 1]}) +data = np.array([(dT, run(dT)) for dT in np.arange(-20, 20, 0.5)]) +df = pd.DataFrame({"t/ms": data[:, 0], "dw": data[:, 1]}) print("Plotting results ...") -seaborn.relplot(data=df, x="t/ms", y="dw", kind="line", ci=None).savefig( +sns.relplot(data=df, x="t/ms", y="dw", kind="line", ci=None).savefig( "single_cell_stdp.svg" ) diff --git a/python/example/single_cell_swc.py b/python/example/single_cell_swc.py index 2a229399396ec79ef948ed8b33ddda8938dcb87b..aa0e2df74f56681ddfaca75843747db2f6dfd76a 100755 --- a/python/example/single_cell_swc.py +++ b/python/example/single_cell_swc.py @@ -11,7 +11,6 @@ # attached to the soma. import arbor -from arbor import mechanism as mech import pandas import seaborn import sys @@ -30,38 +29,34 @@ 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 - } + "axon_end": '(restrict (terminal) (region "axon"))', + } # end of the axon. ).add_swc_tags() # Finally, add the SWC default labels. -decor = arbor.decor() - -# Set initial membrane potential to -55 mV -decor.set_property(Vm=-55) -# Use Nernst to calculate reversal potential for calcium. -decor.set_ion("ca", method=mech("nernst/x=ca")) -# decor.set_ion('ca', method='nernst/x=ca') -# hh mechanism on the soma and axon. -decor.paint('"soma"', arbor.density("hh")) -decor.paint('"axon"', arbor.density("hh")) -# pas mechanism the dendrites. -decor.paint('"dend"', arbor.density("pas")) -# Increase resistivity on dendrites. -decor.paint('"dend"', rL=500) -# Attach stimuli that inject 4 nA current for 1 ms, starting at 3 and 8 ms. -decor.place('"root"', arbor.iclamp(10, 1, current=5), "iclamp0") -decor.place('"stim_site"', arbor.iclamp(3, 1, current=0.5), "iclamp1") -decor.place('"stim_site"', arbor.iclamp(10, 1, current=0.5), "iclamp2") -decor.place('"stim_site"', arbor.iclamp(8, 1, current=4), "iclamp3") -# Detect spikes at the soma with a voltage threshold of -10 mV. -decor.place('"axon_end"', arbor.spike_detector(-10), "detector") - -# Create the policy used to discretise the cell into CVs. -# Use a single CV for the soma, and CVs of maximum length 1 μm elsewhere. -soma_policy = arbor.cv_policy_single('"soma"') -dflt_policy = arbor.cv_policy_max_extent(1.0) -policy = dflt_policy | soma_policy -decor.discretization(policy) +decor = ( + arbor.decor() + # Set initial membrane potential to -55 mV + .set_property(Vm=-55) + # Use Nernst to calculate reversal potential for calcium. + .set_ion("ca", method=arbor.mechanism("nernst/x=ca")) + # hh mechanism on the soma and axon. + .paint('"soma"', arbor.density("hh")) + .paint('"axon"', arbor.density("hh")) + # pas mechanism the dendrites. + .paint('"dend"', arbor.density("pas")) + # Increase resistivity on dendrites. + .paint('"dend"', rL=500) + # Attach stimuli that inject 4 nA current for 1 ms, starting at 3 and 8 ms. + .place('"root"', arbor.iclamp(10, 1, current=5), "iclamp0") + .place('"stim_site"', arbor.iclamp(3, 1, current=0.5), "iclamp1") + .place('"stim_site"', arbor.iclamp(10, 1, current=0.5), "iclamp2") + .place('"stim_site"', arbor.iclamp(8, 1, current=4), "iclamp3") + # Detect spikes at the soma with a voltage threshold of -10 mV. + .place('"axon_end"', arbor.spike_detector(-10), "detector") + # Create the policy used to discretise the cell into CVs. + # Use a single CV for the soma, and CVs of maximum length 1 μm elsewhere. + .discretization('(replace (single (region "soma")) (max-extent 1.0))') +) # Combine morphology with region and locset definitions to make a cable cell. cell = arbor.cable_cell(morpho, labels, decor) diff --git a/scripts/mk-include.py b/scripts/mk-include.py new file mode 100644 index 0000000000000000000000000000000000000000..cb798e678c739525d0e2367196e10350fde4f241 --- /dev/null +++ b/scripts/mk-include.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +# Use this script for convenience when making tutorials +# for readthedocs/sphinx. +# +# Run on a tutorial python file like this +# +# ./mk-include tutorial.py prefix +# +# the script will extract all comments from the +# tutorial script starting with +# +# # (N) +# +# where N is whole number. For each such comment +# it will print out a literal include block for use +# in a sphinx tutorial .rst file, eg +# +# .. literalinclude:: ../../python/example/single_cell_detailed.py +# :language: python +# :lines: 98-102 +# +# The line numbers are chosen such they start at the +# comment '# (N)' and end just before the next such +# comment (or the end of file). +# +# The prefix argument is added to the basename like this +# +# ./mk-include path/to/tutorial.py prefix/of/docs +# +# gives blocks like this +# +# .. literalinclude:: prefix/of/docs/tutorial.py + +import sys +import re +from pathlib import Path + +fn, pf = map(Path, sys.argv[1:]) + +hd, tl, bl, em = 0, 0, None, None +with open(fn) as fd: + for ln in fd: + tl += 1 + m = re.match(r"\s*#\s*\(([0-9]+)\).*", ln) + if m: + if bl: + print( + f"""## Block {bl} + +.. literalinclude:: {pf}/{fn.name} + :language: python + :lines: {hd}-{em} +""" + ) + hd = tl + bl = m.group(1) + if ln.strip(): + em = tl + +if bl: + print( + f"""## Block {bl} + +.. literalinclude:: {pf}/{fn.name} + :language: python + :line: {hd}-{tl} +""" + )