From f95085ceb0417d45afeee070bf95e284021e6891 Mon Sep 17 00:00:00 2001 From: Ben Cumming <bcumming@cscs.ch> Date: Tue, 21 Apr 2020 14:43:31 +0200 Subject: [PATCH] extensions ot morphology/single cell python API and example code (#1019) Split Python wrapper changes and tutorial example from the Python documentation branch. - extend `append` methods for `sample_tree` Python wrapper to accommodate a flatter representation of samples. - add a tutorial example (will make more sense as part of larger python docs) --- python/morphology.cpp | 26 +++++++++++++---- python/single_cell_model.cpp | 4 ++- python/tutorial/example1.py | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 python/tutorial/example1.py diff --git a/python/morphology.cpp b/python/morphology.cpp index 6f991901..4c2047d5 100644 --- a/python/morphology.cpp +++ b/python/morphology.cpp @@ -84,11 +84,10 @@ void register_morphology(pybind11::module& m) { "If loaded from standard SWC file the following tags are used: soma=1, axon=2, dendrite=3, apical dendrite=4, however arbitrary tags can be used.") .def("__str__", [](const arb::msample& s) { - return util::pprintf("<arbor.mpoint: x {}, y {}, z {}, radius {}, tag {}>", - s.loc.x, s.loc.y, s.loc.z, s.loc.radius, s.tag); }) + return util::pprintf("{}", s);}) .def("__repr__", [](const arb::msample& s) { - return util::pprintf("{}>", s);}); + return util::pprintf("{}", s);}); // arb::mcable pybind11::class_<arb::mcable> cable(m, "cable"); @@ -123,8 +122,23 @@ void register_morphology(pybind11::module& m) { .def(pybind11::init<>()) // modifiers .def("reserve", &arb::sample_tree::reserve) - .def("append", [](arb::sample_tree& t, arb::msample s){return t.append(s);}) - .def("append", [](arb::sample_tree& t, arb::msize_t p, arb::msample s){return t.append(p, s);}) + .def("append", [](arb::sample_tree& t, arb::msample s){return t.append(s);}, + "Append a sample whose parent is the last sample added to the tree.") + .def("append", [](arb::sample_tree& t, arb::msize_t p, arb::msample s){return t.append(p, s);}, + "parent"_a, "sample"_a, + "Append a sample.") + .def("append", + [](arb::sample_tree& t, double x, double y, double z, double radius, int tag) { + return t.append(arb::msample{{x,y,z,radius}, tag}); + }, + "x"_a, "y"_a, "z"_a, "radius"_a, "tag"_a, + "Append a sample whose parent is the last sample added to the tree.") + .def("append", + [](arb::sample_tree& t, arb::msize_t p, double x, double y, double z, double radius, int tag) { + return t.append(p, arb::msample{{x,y,z,radius}, tag}); + }, + "parent"_a, "x"_a, "y"_a, "z"_a, "radius"_a, "tag"_a, + "Append a sample.") // properties .def_property_readonly("empty", [](const arb::sample_tree& st){return st.empty();}, "Indicates whether the sample tree is empty (i.e. whether it has size 0)") @@ -132,6 +146,8 @@ void register_morphology(pybind11::module& m) { "The number of samples in the sample tree.") .def_property_readonly("parents", [](const arb::sample_tree& st){return st.parents();}, "A list with the parent index of each sample.") + .def_property_readonly("samples", [](const arb::sample_tree& st){return st.samples();}, + "A list of the samples.") .def("__str__", [](const arb::sample_tree& s) { return util::pprintf("<arbor.sample_tree:\n{}>", s);}); diff --git a/python/single_cell_model.cpp b/python/single_cell_model.cpp index 03647765..ae69e3e2 100644 --- a/python/single_cell_model.cpp +++ b/python/single_cell_model.cpp @@ -229,7 +229,9 @@ void register_single_cell(pybind11::module& m) { .def_readonly("variable", &trace::variable, "Name of the variable being recorded.") .def_readonly("location", &trace::loc, "Location on cell morphology.") .def_readonly("time", &trace::t, "Time stamps of samples [ms].") - .def_readonly("value", &trace::v, "Sample values."); + .def_readonly("value", &trace::v, "Sample values.") + .def("__str__", [](const trace& tr) {return util::pprintf("(trace \"{}\" {})", tr.variable, tr.loc);}) + .def("__repr__", [](const trace& tr) {return util::pprintf("(trace \"{}\" {})", tr.variable, tr.loc);}); pybind11::class_<single_cell_model> model(m, "single_cell_model", "Wrapper for simplified description, and execution, of single cell models."); diff --git a/python/tutorial/example1.py b/python/tutorial/example1.py new file mode 100644 index 00000000..43bfd5c7 --- /dev/null +++ b/python/tutorial/example1.py @@ -0,0 +1,56 @@ +import arbor + +# Create a sample tree with a single sample of radius 3 μm +tree = arbor.sample_tree() +tree.append(arbor.msample(x=0, y=0, z=0, radius=3, tag=2)) + +labels = arbor.label_dict({'soma': '(tag 2)', 'center': '(location 0 0.5)'}) + +cell = arbor.cable_cell(tree, labels) + +# Set initial membrane potential everywhere on the cell to -40 mV. +cell.set_properties(Vm=-40) +# Put hh dynamics on soma, and passive properties on the dendrites. +cell.paint('soma', 'hh') +# Attach stimuli with duration of 2 ms and current of 0.8 nA. +cell.place('center', arbor.iclamp( 10, 2, 0.8)) +# Add a spike detector with threshold of -10 mV. +cell.place('center', arbor.spike_detector(-10)) + +# Make single cell model. +m = arbor.single_cell_model(cell) + +# Attach voltage probes, sampling at 10 kHz. +m.probe('voltage', 'center', 10000) + +# Run simulation for 100 ms of simulated activity. +tfinal=30 +m.run(tfinal) + +# Print spike times. +if len(m.spikes)>0: + print('{} spikes:'.format(len(m.spikes))) + for s in m.spikes: + print(' {:7.4f}'.format(s)) +else: + print('no spikes') + +# Plot the recorded voltages over time. +import matplotlib.pyplot as plt +fig, ax = plt.subplots() +for t in m.traces: + ax.plot(t.time, t.value) + +legend_labels = ['{}: {}'.format(s.variable, s.location) for s in m.traces] +ax.legend(legend_labels) +ax.set(xlabel='time (ms)', ylabel='voltage (mV)', title='cell builder demo') +plt.xlim(0,tfinal) +plt.ylim(-80,50) +ax.grid() + +# Set to True to save the image to file instead of opening a plot window. +plot_to_file=False +if plot_to_file: + fig.savefig("voltages.png", dpi=300) +else: + plt.show() -- GitLab